Definizione delle funzioni del modulo privato in Python


238

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

Come la maggior parte delle lingue, Python ha il concetto di elementi privati:

  • Funzioni private, che non possono essere richiamate dall'esterno del loro modulo

Tuttavia, se definisco due file:

#a.py
__num=1

e:

#b.py
import a
print a.__num

quando b.pylo eseguo stampa 1senza dare alcuna eccezione. Diveintopython è sbagliato o ho frainteso qualcosa? E c'è un modo per fare di definire la funzione di un modulo come privato?


Non è che diveintopython sia sbagliato, ma nel loro esempio: >>> import fileinfo >>> m = fileinfo.MP3FileInfo() >>> m.__parse("/music/_singles/kairo.mp3") 1 Traceback (innermost last): File "<interactive input>", line 1, in ? AttributeError: 'MP3FileInfo' instance has no attribute '__parse' fileinfo.MP3FileInfo () è un'istanza di classe. Che dà questa eccezione quando si usa il doppio trattino basso. Mentre nel tuo caso non hai creato una classe, hai semplicemente creato un modulo. Vedi anche: stackoverflow.com/questions/70528/…
Homero Esmeraldo

Risposte:


323

In Python, la "privacy" dipende dai livelli di accordo degli "adulti consenzienti" - non puoi forzarlo (come non puoi nella vita reale ;-). Un singolo trattino di sottolineatura iniziale significa che non dovresti accedervi "dall'esterno" - due trattini di sottolineatura (senza sottolineature finali) portano il messaggio in modo ancora più energico ... ma, alla fine, dipende ancora dai social convenzione e consenso: l'introspezione di Python è abbastanza forte da non poter ammanettare tutti gli altri programmatori del mondo per rispettare i tuoi desideri.

((A proposito, anche se è un segreto molto stretto, lo stesso vale per C ++: con la maggior parte dei compilatori, una semplice #define private publicriga prima di #includeinserire il tuo .hfile è tutto ciò che serve ai programmatori astuti per fare hash della tua "privacy" ...! -) )


82
La tua nota su C ++ non è corretta. Usando #define public private stai cambiando il codice che viene inviato al compilatore, che è dove ha luogo il nome mangling.
rinoceronte

14
Anche la menomazione del C ++ è oscura, ma quasi segreta. Puoi "introspettare" anche un binario prodotto da C ++. OT, scusa.
Prof. Falken,

47
Come aggiornamento di @rhinoinrepose, non è solo errato, è un comportamento indefinito secondo lo standard per ridefinire una parola chiave con una macro di preprocessore.
Cory Kramer,

3
@AlexMartelli Non è static void foo()così privato come sembra. È almeno nascosto al linker e la funzione può essere rimossa interamente mediante allineamento.
user877329

3
Nella vita reale le persone vengono perseguite se violano le leggi
Laici González,

289

Potrebbe esserci confusione tra privati ​​di classe e privati ​​di modulo .

Un modulo privato inizia con un carattere di sottolineatura
Tale elemento non viene copiato quando si utilizza il from <module_name> import *modulo del comando import; viene comunque importato se si utilizza la import <moudule_name>sintassi ( vedere la risposta di Ben Wilhelm )
Basta rimuovere un carattere di sottolineatura dal numero a .__ dell'esempio della domanda e non verrà mostrato nei moduli che importano a.py usando la from a import *sintassi.

Un privato di classe inizia con due caratteri di sottolineatura (ovvero dunder o doppio punteggio inferiore).
Tale variabile ha il suo nome "alterato" per includere il nome
della classe ecc. È ancora possibile accedervi al di fuori della logica della classe, tramite il nome modificato.
Sebbene la manipolazione dei nomi possa servire come un lieve dispositivo di prevenzione contro l'accesso non autorizzato, il suo scopo principale è prevenire possibili collisioni di nomi con membri della classe delle classi antenate. Vedi il riferimento divertente ma accurato di Alex Martelli agli adulti consenzienti mentre descrive la convenzione usata riguardo a queste variabili.

>>> class Foo(object):
...    __bar = 99
...    def PrintBar(self):
...        print(self.__bar)
...
>>> myFoo = Foo()
>>> myFoo.__bar  #direct attempt no go
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__bar'
>>> myFoo.PrintBar()  # the class itself of course can access it
99
>>> dir(Foo)    # yet can see it
['PrintBar', '_Foo__bar', '__class__', '__delattr__', '__dict__', '__doc__', '__
format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__
', '__subclasshook__', '__weakref__']
>>> myFoo._Foo__bar  #and get to it by its mangled name !  (but I shouldn't!!!)
99
>>>

Bene, TIL. Qualche motivo per cui non impongono a livello di modulo __private_function, però? Mi sono imbattuto in questo e ho avuto errori a causa sua.
Santa

Grazie per le belle parole @Terrabits, ma sono felice di essere secondo ad Alex (ben dietro!) Su tutte le cose "Python". Inoltre, le sue risposte sono in genere più concise e divertenti, pur mantenendo un alto livello di autorevolezza dato ad Alex un ampio contributo alla lingua e alla comunità.
mjv,

1
@mjv Questa è stata una spiegazione così utile! Grazie! Sono stato abbastanza perplesso su questo comportamento per un po '. Vorrei che la scelta fosse stata quella di lanciare un qualche tipo di errore diverso da un AttributeError se si fosse tentato di accedere direttamente alla classe privata; forse un "PrivateAccessError" o qualcosa sarebbe stato più esplicito / utile. (Dal momento che ottenere l'errore che non ha un attributo non è proprio vero).
HFBrowning

82

A questa domanda non è stata data una risposta completa, poiché la privacy del modulo non è puramente convenzionale e poiché l'utilizzo dell'importazione può o meno riconoscere la privacy del modulo, a seconda di come viene utilizzato.

Se si definiscono nomi privati ​​in un modulo, tali nomi verranno importati in qualsiasi script che utilizza la sintassi, "import nome_modulo". Quindi, supponendo che tu abbia definito correttamente nel tuo esempio il modulo private, _num, in a.py, in questo modo ..

#a.py
_num=1

..si sarebbe in grado di accedervi in ​​b.py con il simbolo del nome del modulo:

#b.py
import a
...
foo = a._num # 1

Per importare solo non privati ​​da a.py, è necessario utilizzare la sintassi from :

#b.py
from a import *
...
foo = _num # throws NameError: name '_num' is not defined

Per motivi di chiarezza, tuttavia, è meglio essere espliciti quando si importano nomi dai moduli, piuttosto che importarli tutti con un '*':

#b.py
from a import name1 
from a import name2
...

1
dove si specifica quali funzioni / librerie vengono importate? nel init .py?
FistOfFury

Non vi è alcun rischio di collisioni di nomi quando _namesvengono invocate import a: sono accessi come a._namesquando si utilizza questo stile.
Josiah Yoder,

@FistOfFury Sì, si specificano le funzioni importate nel __init__.pyfile. Vedi qui per un aiuto su questo.
Mike Williamson,

29

Python consente lezioni private membri di con il doppio prefisso di sottolineatura. Questa tecnica non funziona a livello di modulo, quindi sto pensando che questo sia un errore in Dive Into Python.

Ecco un esempio di funzioni di classe private:

class foo():
    def bar(self): pass
    def __bar(self): pass

f = foo()
f.bar()   # this call succeeds
f.__bar() # this call fails

2
Penso che l'intenzione del PO sia quella di scrivere funzioni che non sono accessibili al di fuori, ad esempio, di un pacchetto commerciale. A tale proposito, questa risposta non è completa. La funzione __bar () è ancora accessibile dall'esterno tramite f._foo__bar (). Pertanto, i caratteri di sottolineatura a doppio vantaggio non lo rendono privato.
SevakPrime,

24

È possibile aggiungere una funzione interna:

def public(self, args):
   def private(self.root, data):
       if (self.root != None):
          pass #do something with data

Qualcosa del genere se hai davvero bisogno di quel livello di privacy.


9
Perché questa non è la risposta migliore?
safay


1

incorporato con chiusure o funzioni è un modo. Questo è comune in JS sebbene non sia richiesto per piattaforme non browser o browser worker.

In Python sembra un po 'strano, ma se qualcosa ha davvero bisogno di essere nascosto potrebbe essere la soluzione. Più precisamente usare l'API di Python e mantenere le cose che richiedono di essere nascoste nella C (o in altro linguaggio) è probabilmente il modo migliore. In caso contrario, inserirò il codice in una funzione, chiamandolo e facendolo restituire gli elementi che si desidera esportare.


-11

Python ha tre modalità tramite., Privata, pubblica e protetta. Durante l'importazione di un modulo è accessibile solo la modalità pubblica. Quindi i moduli privati ​​e protetti non possono essere richiamati dall'esterno del modulo, cioè quando viene importato.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.