it-swarm-eu.dev

Perché i metodi "privati" di Python non sono in realtà privati?

Python ci dà la possibilità di creare metodi e variabili "privati" all'interno di una classe, anteponendo due trattini bassi al nome, in questo modo: __myPrivateMethod(). Come, quindi, si può spiegare questo

>>> class MyClass:
...     def myPublicMethod(self):
...             print 'public method'
...     def __myPrivateMethod(self):
...             print 'this is private!!'
... 
>>> obj = MyClass()
>>> obj.myPublicMethod()
public method
>>> obj.__myPrivateMethod()
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: MyClass instance has no attribute '__myPrivateMethod'
>>> dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']
>>> obj._MyClass__myPrivateMethod()
this is private!!

Qual è l'accordo?!

Te lo spiego un po 'per quelli che non l'hanno capito.

>>> class MyClass:
...     def myPublicMethod(self):
...             print 'public method'
...     def __myPrivateMethod(self):
...             print 'this is private!!'
... 
>>> obj = MyClass()

Quello che ho fatto è creare una classe con un metodo pubblico e un metodo privato e istanziarla.

Successivamente, chiamo il suo metodo pubblico.

>>> obj.myPublicMethod()
public method

Successivamente, provo a chiamare il suo metodo privato.

>>> obj.__myPrivateMethod()
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: MyClass instance has no attribute '__myPrivateMethod'

Tutto sembra a posto qui; non siamo in grado di chiamarlo. È, infatti, "privato". Beh, in realtà non lo è. Esecuzione dir () sull'oggetto rivela un nuovo metodo magico che Python crea magicamente per tutti i tuoi metodi 'privati'.

>>> dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']

Il nome di questo nuovo metodo è sempre un trattino basso, seguito dal nome della classe, seguito dal nome del metodo.

>>> obj._MyClass__myPrivateMethod()
this is private!!

Così tanto per l'incapsulamento, eh?

In ogni caso, ho sempre sentito che Python non supporta l'incapsulamento, quindi perché provare? Cosa dà?

557
willurd

Il nome scrambling viene utilizzato per garantire che le sottoclassi non ignorino accidentalmente i metodi e gli attributi privati ​​delle loro superclassi. Non è progettato per impedire l'accesso intenzionale dall'esterno.

Per esempio:

>>> class Foo(object):
...     def __init__(self):
...         self.__baz = 42
...     def foo(self):
...         print self.__baz
...     
>>> class Bar(Foo):
...     def __init__(self):
...         super(Bar, self).__init__()
...         self.__baz = 21
...     def bar(self):
...         print self.__baz
...
>>> x = Bar()
>>> x.foo()
42
>>> x.bar()
21
>>> print x.__dict__
{'_Bar__baz': 21, '_Foo__baz': 42}

Naturalmente, si rompe se due classi diverse hanno lo stesso nome.

524
Alya

Esempio di funzione privata

import re
import inspect

class MyClass :

    def __init__(self) :
        pass

    def private_function ( self ) :
        try :
            function_call = inspect.stack()[1][4][0].strip()

            # See if the function_call has "self." in the begining
            matched = re.match( '^self\.', function_call )
            if not matched :
                print 'This is Private Function, Go Away'
                return
        except :
            print 'This is Private Function, Go Away'
            return

        # This is the real Function, only accessible inside class #
        print 'Hey, Welcome in to function'

    def public_function ( self ) :
        # i can call private function from inside the class
        self.private_function()

### End ###
206
arun

Quando sono arrivato da Java a Python I odiato questo. Mi ha spaventato a morte.

Oggi potrebbe essere l'unica cosa I love most su Python.

Adoro stare su una piattaforma, dove le persone si fidano l'una dell'altra e non sentono il bisogno di costruire muri impenetrabili attorno al loro codice. In linguaggi fortemente incapsulati, se un'API ha un bug e hai capito cosa non va, potresti ancora non essere in grado di aggirarlo perché il metodo necessario è privato. In Python l'atteggiamento è: "sicuro". Se pensi di capire la situazione, forse l'hai anche letta, allora tutto quello che possiamo dire è "buona fortuna!".

Ricorda, l'incapsulamento non è nemmeno debolmente correlato alla "sicurezza", o impedisce ai bambini di uscire dal prato. È solo un altro modello che dovrebbe essere usato per rendere più facile capire un codice base.

141
Thomas Ahle

Da http://www.faqs.org/docs/diveintopython/fileinfo_private.html

A rigor di termini, i metodi privati ​​sono accessibile al di fuori della loro classe, solo non facilmente accessibile. Niente in Python è veramente privato; internamente, i nomi dei metodi privati ​​e gli attributi sono maciullati e liberi al volo per farli sembrare inaccessibile dai loro nomi dati. Tu può accedere al metodo __parse del MP3FileInfo classe dal nome _MP3FileInfo__parse. Riconoscere che questo è interessante, quindi promettere di mai, mai farlo nel codice reale . I metodi privati ​​sono privati ​​per un ragione, ma come molte altre cose in Python, la loro privateness è in definitiva una questione di convenzione, non vigore.

134
xsl

La frase comunemente usata è "siamo tutti adulti consenzienti qui". Prevedendo un singolo underscore (non esporre) o double underscore (nascondi), stai dicendo all'utente della tua classe che intendi che il membro sia "privato" in qualche modo. Tuttavia, ti stai fidando che tutti gli altri si comportino in modo responsabile e rispettano questo, a meno che non abbiano un valido motivo per non farlo (ad es. Debugger, completamento del codice).

Se davvero devi avere qualcosa che è privato, puoi implementarlo in un'estensione (ad esempio in C per CPython). Nella maggior parte dei casi, tuttavia, impari semplicemente il modo Python di fare le cose.

86
Tony Meyer

Non è che tu non sia assolutamente in grado di aggirare la privacy dei membri in qualsiasi lingua (puntatore aritmetico in C++, Reflections in .NET/Java).

Il punto è che ricevi un errore se provi a chiamare per errore il metodo privato. Ma se vuoi spararti ai piedi, vai avanti e fallo.

Modifica: non cerchi di proteggere i tuoi contenuti con l'incapsulamento OO, vero?

32
Maximilian

Comportamento simile esiste quando i nomi degli attributi dei moduli iniziano con un singolo trattino basso (ad es. _Foo).

Gli attributi del modulo nominati come tali non verranno copiati in un modulo di importazione quando si utilizza il metodo from*, ad esempio:

from bar import *

Tuttavia, questa è una convenzione e non un vincolo di linguaggio. Questi non sono attributi privati; possono essere referenziati e manipolati da qualsiasi importatore. Alcuni sostengono che a causa di ciò, Python non può implementare l'incapsulamento vero.

12
Ross

È solo una di quelle scelte di design linguistico. A un certo livello sono giustificati. Loro fanno in modo che tu debba andare molto lontano per provare a chiamare il metodo, e se ne hai davvero bisogno, devi avere una buona ragione!

Debugging ganci e test vengono in mente come possibili applicazioni, ovviamente utilizzate in modo responsabile.

12
ctcherry

La convenzione di denominazione class.__stuff consente al programmatore di sapere che non è previsto l'accesso a __stuff dall'esterno. Il nome mangling rende improbabile che qualcuno lo faccia per caso.

È vero, puoi ancora aggirare il problema, è ancora più facile che in altre lingue (che anche BTW ti permette di fare), ma nessun programmatore Python lo farebbe se si preoccupasse dell'incapsulamento.

11
Nickolay

Con Python 3.4 questo è il comportamento:

>>> class Foo:
        def __init__(self):
                pass
        def __privateMethod(self):
                return 3
        def invoke(self):
                return self.__privateMethod()


>>> help(Foo)
Help on class Foo in module __main__:

class Foo(builtins.object)
 |  Methods defined here:
 |
 |  __init__(self)
 |
 |  invoke(self)
 |
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |
 |  __dict__
 |      dictionary for instance variables (if defined)
 |
 |  __weakref__
 |      list of weak references to the object (if defined)

 >>> f = Foo()
 >>> f.invoke()
 3
 >>> f.__privateMethod()
 Traceback (most recent call last):
   File "<pyshell#47>", line 1, in <module>
     f.__privateMethod()
 AttributeError: 'Foo' object has no attribute '__privateMethod'

https://docs.python.org/3/tutorial/classes.html#tut-private

Si noti che le regole di mangling sono progettate principalmente per evitare incidenti; è ancora possibile accedere o modificare una variabile considerata privata. Questo può anche essere utile in circostanze particolari, come nel debugger.

Anche se la domanda è vecchia, spero che il mio frammento possa essere utile.

3
Alberto

La preoccupazione più importante sui metodi e gli attributi privati ​​è quella di dire agli sviluppatori di non chiamarlo al di fuori della classe e questo è l'incapsulamento. si può fraintendere la sicurezza dall'incapsulamento. quando si usa deliberatamente una sintassi come quella (qui sotto) che hai citato, non vuoi l'incapsulamento.

obj._MyClass__myPrivateMethod()

Sono migrato da C # e inizialmente è stato strano anche per me, ma dopo un po 'mi sono reso conto che il modo in cui i designer di codice Python pensano a OOP è diverso. 

2
Afshin Amiri

Perché i metodi "privati" di Python non sono in realtà privati?

A quanto ho capito, loro non possono essere privati. Come si potrebbe applicare la privacy?

La risposta ovvia è che "i membri privati ​​sono accessibili solo tramite self", ma ciò non funzionerebbe - self non è speciale in Python, non è altro che un nome comunemente usato per il primo parametro di una funzione.

0
user200783