Python ha variabili "private" nelle classi?


578

Vengo dal mondo Java e sto leggendo Python 3 Patterns, Recipes and Idioms di Bruce Eckels .

Durante la lettura delle classi, si continua dicendo che in Python non è necessario dichiarare le variabili di istanza. Li usi solo nel costruttore, e boom, sono lì.

Quindi per esempio:

class Simple:
    def __init__(self, s):
        print("inside the simple constructor")
        self.s = s

    def show(self):
        print(self.s)

    def showMsg(self, msg):
        print(msg + ':', self.show())

Se questo è vero, allora qualsiasi oggetto della classe Simplepuò semplicemente cambiare il valore della variabile sal di fuori della classe.

Per esempio:

if __name__ == "__main__":
    x = Simple("constructor argument")
    x.s = "test15" # this changes the value
    x.show()
    x.showMsg("A message")

In Java, ci hanno insegnato le variabili pubbliche / private / protette. Queste parole chiave hanno senso perché a volte vuoi variabili in una classe a cui nessuno al di fuori della classe ha accesso.

Perché non è necessario in Python?


17
Intendevi variabili di istanza , non variabili di classe , giusto?
PaulMcG,

13
Dovresti controllare le proprietà: docs.python.org/library/functions.html#property . Usa semplicemente il getter e la tua variabile sarà protetta.
rubik,

Una risposta breve e chiara è qui . Spero che questo possa aiutare.
Premkumar chalmeti,

Risposte:


962

È culturale. In Python, non scrivi nell'istanza o nelle variabili di classe di altre classi. In Java, nulla vieta di fare lo stesso se si veramente vuole - dopo tutto, è sempre possibile modificare l'origine della classe stessa per ottenere lo stesso effetto. Python elimina questa pretesa di sicurezza e incoraggia i programmatori a essere responsabili. In pratica, funziona molto bene.

Se si desidera emulare variabili private per qualche motivo, è sempre possibile utilizzare il __prefisso di PEP 8 . Python storpia i nomi di variabili come __fooin modo che essi non sono facilmente visibili al codice esterno alla classe che le contiene (anche se è possibile aggirare l'ostacolo se siete abbastanza determinato, proprio come si può aggirare le protezioni di Java se si lavora in esso ).

Secondo la stessa convenzione, il _prefisso significa stare alla larga anche se tecnicamente non lo si può fare . Non giochi con le variabili di un'altra classe che sembrano __fooo _bar.


14
Questo ha senso. Tuttavia, non penso che ci sia alcun modo in Java per accedere a variabili private al di fuori della classe (tranne cambiare effettivamente l'origine della classe di corso). È lì?
Onnipresente il

168
Tendo a preferire la via del pitone, ma non penso che la via java sia inutile come te la capisci. Dichiarare qualcosa di privato dice rapidamente a qualcuno che legge il codice qualcosa di molto utile: questo campo è sempre e solo modificato all'interno di questa classe.
Ned

67
@Omnipresent, puoi usare la riflessione.
rapadura,

77
Consentitemi di chiarire le cose, quindi Python non implementa attributi pubblici o privati ​​perché "è una finzione di sicurezza e incoraggia i programmatori a essere responsabili", tuttavia la comunità incoraggia l'uso di "_" per indicare variabili e metodi privati? Forse Python dovrebbe avere sicuramente no pubblico e privato? Il loro scopo principale è di dirti quale API dovresti usare per interagire con una classe. Servono come documentazione che ti dice di usare questi metodi e di non usarli. Non sono una "pretesa di sicurezza", sono una documentazione API, che può anche essere utilizzata dall'IDE per guidarti!
PedroD,

19
Questa è una buona risposta e il tuo ragionamento è sicuramente valido, ma non sono d'accordo con un aspetto. Lo scopo dei modificatori di accesso non è mai stato la sicurezza . Piuttosto, sono un mezzo per delimitare esplicitamente (e in larga misura, applicare) quali parti di una classe sono considerate interne e quali sono esposte agli utenti esterni di quella classe. Le convenzioni (cultura) sono certamente una valida alternativa ai modificatori di accesso, ed entrambi i metodi hanno i loro pro e contro, ma è fuorviante lo scopo che i modificatori di accesso a livello di lingua siano intesi in qualche modo "sicuri" nel senso comune del parola.
devios1

159

Le variabili private in Python sono più o meno un hack: l'interprete rinomina intenzionalmente la variabile.

class A:
    def __init__(self):
        self.__var = 123
    def printVar(self):
        print self.__var

Ora, se provi ad accedere __varal di fuori della definizione della classe, fallirà:

 >>>x = A()
 >>>x.__var # this will return error: "A has no attribute __var"

 >>>x.printVar() # this gives back 123

Ma puoi facilmente cavartela con questo:

 >>>x.__dict__ # this will show everything that is contained in object x
               # which in this case is something like {'_A__var' : 123}

 >>>x._A__var = 456 # you now know the masked name of private variables
 >>>x.printVar() # this gives back 456

Probabilmente sai che i metodi in OOP sono invocati in questo modo:, x.printVar() => A.printVar(x)se è A.printVar()possibile accedere a un campo in x, questo campo è anche accessibile all'esterno A.printVar() ... dopo tutto, le funzioni vengono create per la riusabilità, non vi è alcun potere speciale dato alle istruzioni all'interno.

Il gioco è diverso quando è coinvolto un compilatore (la privacy è un concetto a livello di compilatore ). Conosce la definizione di classe con modificatori del controllo di accesso, quindi può sbagliare se le regole non vengono seguite al momento della compilazione


3
in breve, non si tratta di incapsulamento
watashiSHUN il

2
Mi chiedo se PHP abbia qualcosa di simile con le sue sciocche variabili private - dal momento che le variabili private non hanno davvero senso nel linguaggio interpretato - intendo quale ottimizzazione può fare sapendo che la variabile x è privata, se non viene compilata?
NoBugs il

1
Come possiamo randomizzare il modello di variabili private?
crisron

@crisron stessa domanda
IanS,

5
@watashiSHUN "in breve, questo non è incapsulamento" => sì, lo è. L'incapsulamento riguarda solo l'utilizzo dell'API pubblica, quindi il codice client è protetto dalle modifiche all'implementazione. Le convenzioni di denominazione sono un modo perfettamente valido per dire cos'è l'API e cos'è l'implementazione e il punto è che funziona.
bruno desthuilliers,

30

Come correttamente menzionato da molti dei commenti sopra, non dimentichiamo l'obiettivo principale dei modificatori di accesso: aiutare gli utenti di codice a capire cosa dovrebbe cambiare e cosa non dovrebbe. Quando vedi un campo privato, non ci si scherza. Quindi è principalmente zucchero sintattico che può essere facilmente raggiunto in Python da _ e __.


4
Penso che questo sia un punto importante come un altro. Quando eseguo il debug del codice (lo so, sono un debole per introdurre i bug), sapere quali classi possono cambiare una variabile membro semplifica il processo di debug. Almeno, se la variabile è protetta da un certo ambito. Un concetto simile è const const in C ++. Io so che le variabili membro non sono stati cambiati in là e quindi non so nemmeno guardo quel metodo come il potenziale causa di una regolazione variabile di male. Sebbene possa rendere successivo lo sviluppo di estensioni di classe / aggiunta di funzionalità, la limitazione della visibilità del codice semplifica il debug.
MrMas

18

Esiste una variazione di variabili private nella convenzione di sottolineatura.

In [5]: class Test(object):
   ...:     def __private_method(self):
   ...:         return "Boo"
   ...:     def public_method(self):
   ...:         return self.__private_method()
   ...:     

In [6]: x = Test()

In [7]: x.public_method()
Out[7]: 'Boo'

In [8]: x.__private_method()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-8-fa17ce05d8bc> in <module>()
----> 1 x.__private_method()

AttributeError: 'Test' object has no attribute '__private_method'

Ci sono alcune sottili differenze, ma per motivi di programmazione del modello di purezza ideologica, è abbastanza buono.

Ci sono esempi di decoratori @private che implementano più da vicino il concetto, ma YMMV. Probabilmente si potrebbe anche scrivere una definizione di classe che utilizza meta


14
Mi rendo conto che è abbastanza tardi per la festa, ma questo link appare su Google quando si cerca su Google il problema. Questo non racconta tutta la storia. __xpoiché una variabile all'interno della classe Aviene effettivamente riscritta dal compilatore _A__x, non è ancora completamente privata e può ancora essere raggiunta.
Zorf,

1
Naturalmente, se vedo una variabile denominata _A__x, non la toccherò. Potrebbe essere contagioso. Scapperò via.
Mateen Ulhaq,

Sicuramente non è un vero privato. Ma il ragionamento principale per il privato forzato in C ++ e Java (ecc.), L'ottimizzazione del compilatore, non esiste davvero in Python, quindi il privato per convenzione è abbastanza buono. La convenzione di Python generalmente è che confida nel fatto che ti comporterai senza supervisione. (Ed è una trappola per principianti, ma sai, basta essere attenti al design e al consumo di classe)
Shayne

14

"In java ci hanno insegnato le variabili pubbliche / private / protette"

"Perché non è necessario in Python?"

Per lo stesso motivo, non è richiesto in Java.

Sei libero di usare - o non usare privateeprotected .

Come programmatore Python e Java, l'ho scoperto privatee protectedsono concetti di design molto, molto importanti. Ma in pratica, in decine di migliaia di linee di Java e Python, non ho mai realmente usato privateoprotected .

Perchè no?

Ecco la mia domanda "protetta da chi?"

Altri programmatori nella mia squadra? Hanno la fonte. Cosa significa protetto quando possono cambiarlo?

Altri programmatori su altri team? Lavorano per la stessa compagnia. Possono - con una telefonata - ottenere la fonte.

Clienti? È una programmazione di lavoro a noleggio (in genere). I client (generalmente) possiedono il codice.

Quindi, da chi - precisamente - lo sto proteggendo?


119
-1: sono d'accordo con Porculus. Non si tratta di vietare l'accesso o nascondere qualcosa, si tratta di documentazione API implicita . Gli sviluppatori così come i compilatori / interpreti / controllori di codice vedono facilmente quali membri si consiglia di utilizzare e quali non devono essere toccati (o almeno con cura). Nella maggior parte dei casi sarebbe un disastro orribile se tutti i membri di una classe o di un modulo fossero pubblici. Considera la distinzione dei membri privati ​​/ protetti / pubblici come un servizio, dicendo: "Ehi, questi membri sono importanti mentre sono usati internamente e probabilmente non sono utili per te".
Oben Sonne,

7
@ S.Lott: sono d'accordo che i documenti API hanno una priorità più alta e spesso sono l'unico modo per comunicare gli usi previsti di un'API. Ma a volte i nomi dei membri e la visibilità (in termini di privato / pubblico) parlano sufficientemente da soli. Inoltre, vedo il tuo punto che l'idea della documentazione implicita non funziona bene negli editor senza ispezione API ma è davvero utile negli IDE con il completamento del codice. Supponendo che tu abbia letto i documenti API già qualche tempo fa, ti aiuta a ricordare come usare una classe. Le cose non funzionerebbero così intelligenti se non ci fosse distinzione tra membri privati ​​e pubblici.
Oben Sonne,

21
In ritardo alla discussione, ma tutto ciò che Porculus e Oben stanno richiedendo qui è gestito in modo perfettamente adeguato dalla convenzione "prefisso con una sottolineatura" (e senza il danno che l'applicazione del compilatore di tale convenzione può causare)
ncoghlan

38
@ S.Lott Non sono un pitone, quindi non commenterò da quella prospettiva. Tuttavia, come sviluppatore Java questo è un consiglio davvero terrificante. -1
dbyrne,

11
Wow. Ti manca completamente il punto, dai un pessimo consiglio, insulti chiunque non sia d'accordo con te su questo punto, ma ottieni ancora badge e più di 1000 punti reputazione per questa "risposta".
Eric Duminil,

12

Come accennato in precedenza, è possibile indicare che una variabile o un metodo è privato anteponendolo a un trattino basso. Se non ti sembra sufficiente, puoi sempre usare il propertydecoratore. Ecco un esempio:

class Foo:

    def __init__(self, bar):
        self._bar = bar

    @property
    def bar(self):
        """Getter for '_bar'."""
        return self._bar

In questo modo, qualcuno o qualcosa a cui fa riferimento barfa effettivamente riferimento al valore restituito della barfunzione anziché alla variabile stessa, pertanto è possibile accedervi ma non modificarli. Tuttavia, se qualcuno lo desiderasse davvero, potrebbe semplicemente utilizzare _bare assegnare un nuovo valore ad esso. Non esiste un modo infallibile per impedire a qualcuno di accedere a variabili e metodi che si desidera nascondere, come è stato detto più volte. Tuttavia, l'utilizzo propertyè il messaggio più chiaro a cui è possibile inviare che una variabile non deve essere modificata. propertypuò essere utilizzato anche per percorsi di accesso getter / setter / deleter più complessi, come spiegato qui: https://docs.python.org/3/library/functions.html#property


Anche Django lo apprezza.
babygame0ver

10

Python ha un supporto limitato per identificatori privati, attraverso una funzione che antepone automaticamente il nome della classe a tutti gli identificatori che iniziano con due caratteri di sottolineatura. Questo è trasparente per il programmatore, per la maggior parte, ma l'effetto netto è che qualsiasi variabile chiamata in questo modo può essere usata come variabile privata.

Vedi qui per saperne di più.

In generale, l'implementazione di Python dell'orientamento agli oggetti è un po 'primitiva rispetto ad altri linguaggi. Ma mi piace questo, in realtà. È un'implementazione molto concettualmente semplice e si adatta bene allo stile dinamico del linguaggio.


Sì. Il bello è che le capacità di metaprogrammazione di Python significano che puoi effettivamente implementare le cose fantasiose se vuoi (E ci sono librerie che implementano decoratori e cose @ private / @ protette / etc. Inferno ho persino visto una libreria che implementava le classi prototipo in stile JS per nessuna maledetta ragione), ma in pratica non è proprio necessario ... Odio il meme "python / js / qualunque cosa sia un lisp" perché non è quasi mai vero, ma Python condivide le costolette di metaprogrammazione combinate con una sintassi semplice "con quella lingua
Shayne,

8

L'unica volta che uso mai variabili private è quando devo fare altre cose quando scrivo o leggo dalla variabile e come tale ho bisogno di forzare l'uso di un setter e / o getter.

Ancora una volta questo vale per la cultura, come già affermato. Ho lavorato a progetti in cui la lettura e la scrittura di variabili di altre classi era gratuita per tutti. Quando un'implementazione è diventata obsoleta, è stato necessario molto più tempo per identificare tutti i percorsi di codice che utilizzavano quella funzione. Quando è stato forzato l'uso di setter e getter, è possibile scrivere facilmente un'istruzione di debug per identificare che il metodo obsoleto era stato chiamato e il percorso del codice che lo chiama.

Quando ti trovi in ​​un progetto in cui chiunque può scrivere un'estensione, notificare agli utenti i metodi obsoleti che devono scomparire in alcune versioni, quindi è fondamentale per ridurre al minimo la rottura del modulo durante gli aggiornamenti.

Quindi la mia risposta è; se tu e i tuoi colleghi mantenete un semplice set di codici, non è sempre necessario proteggere le variabili di classe. Se stai scrivendo un sistema estensibile, diventa indispensabile quando vengono apportate modifiche al core che deve essere catturato da tutte le estensioni utilizzando il codice.


8

Scusate ragazzi per "resuscitare" il thread, ma spero che questo possa aiutare qualcuno:

In Python3 se vuoi solo "incapsulare" gli attributi della classe, come in Java, puoi semplicemente fare la stessa cosa in questo modo:

class Simple:
    def __init__(self, str):
        print("inside the simple constructor")
        self.__s = str

    def show(self):
        print(self.__s)

    def showMsg(self, msg):
        print(msg + ':', self.show())

Per creare un'istanza, procedere come segue:

ss = Simple("lol")
ss.show()

Nota che: print(ss.__s)genererà un errore.

In pratica, Python3 offuscherà il nome dell'attributo globale. Trasformandolo come un attributo "privato", come in Java. Il nome dell'attributo è ancora globale, ma in modo inaccessibile, come un attributo privato in altre lingue.

Ma non averne paura. Non importa Fa anche il lavoro. ;)


2
questo esiste da Python 1.5.2 IIRC e non impedisce ancora l'accesso all'attributo tramite il suo nome alterato.
bruno desthuilliers,

7

i concetti privati ​​e protetti sono molto importanti. Ma python - solo uno strumento per la prototipazione e lo sviluppo rapido con risorse limitate disponibili per lo sviluppo, ecco perché alcuni dei livelli di protezione non sono seguiti così rigorosamente in Python. Puoi usare "__" nel membro della classe, funziona correttamente, ma non sembra abbastanza buono - ogni accesso a tale campo contiene questi caratteri.

Inoltre, puoi notare che il concetto OOP in pitone non è perfetto, smaltalo o rubino molto più vicino al concetto OOP puro. Anche C # o Java sono più vicini.

Python è un ottimo strumento. Ma è un linguaggio OOP semplificato. Sintatticamente e concettualmente semplificato. L'obiettivo principale dell'esistenza di Python è offrire agli sviluppatori la possibilità di scrivere codice facilmente leggibile con un alto livello di astrazione in modo molto veloce.


Il Rearson Private e Protected sono importanti è che nelle lingue compilate staticamente il compilatore può creare chiamate diirect al metodo privato, ma deve fare affidamento su una tabella di ricerca per i metodi pubblici. Questo non è semplicemente un problema con i linguaggi dinamici. Finalmente linguaggi come il C ++ ci sono implicazioni per l'ereditarietà e la risoluzione del metodo. Python e Ruby hanno implementazioni molto simili di OO, quindi il confronto non ha senso. Smalltalk in realtà non ha alcuna nozione di messaggi pubblici / privati. Puoi aggiungere il privato come categoria, ma è puramente consultivo.
Shayne,

Per favorire la mia affermazione. Sfruttare un punto di vista sull'igiene codificante, sì, sono importanti per l'incapsulamento, ma non è necessario per esso, e quindi i decoratori di @private (ecc.) Sono più consultivi di ogni altra cosa, ma poiché privato / pubblico non aggiunge nulla di utile all'ottimizzazione in un linguaggio non statico, non è implementato a un livello profondo come in un linguaggio compilato come Java o C
Shayne,

4

Python non ha variabili private come C ++ o Java. Se lo desideri, puoi accedere a qualsiasi variabile membro in qualsiasi momento. Tuttavia, non hai bisogno di variabili private in Python, perché in Python non è male esporre le variabili membro delle classi. Se hai la necessità di incapsulare una variabile membro, puoi farlo utilizzando "@property" in seguito senza rompere il codice client esistente.

In Python il singolo carattere di sottolineatura "_" viene utilizzato per indicare che un metodo o una variabile non sono considerati parte dell'API pubblica di una classe e che questa parte dell'API potrebbe cambiare tra versioni diverse. È possibile utilizzare questi metodi / variabili, ma il codice potrebbe non funzionare se si utilizza una versione più recente di questa classe.

Il doppio trattino basso "__" non significa una "variabile privata". Lo si utilizza per definire variabili che sono "class local" e che non possono essere facilmente ignorate dalle sottoclassi. Mangia il nome delle variabili.

Per esempio:

class A(object):
    def __init__(self):
        self.__foobar = None # will be automatically mangled to self._A__foobar

class B(A):
    def __init__(self):
        self.__foobar = 1 # will be automatically mangled to self._B__foobar

self .__ Il nome di foobar viene automaticamente modificato in self._A__foobar in classe A. Nella classe B viene modificato in self._B__obobar. Quindi ogni sottoclasse può definire la propria variabile __foobar senza sovrascrivere le variabili dei genitori. Nulla ti impedisce di accedere alle variabili a partire da sottolineature doppie. Tuttavia, la modifica del nome ti impedisce di chiamare queste variabili / metodi per inciso.

Consiglio vivamente di guardare Raymond Hettingers mentre parla del "toolkit di sviluppo della classe Pythons" di Pycon 2013 (dovrebbe essere disponibile su Youtube), che fornisce un buon esempio del perché e di come utilizzare @property e "__" - variabili di istanza.


Vado a dare un'occhiata a quel discorso. Fa @propertyparte dello standard Python o è specifico di un IDE?
bballdave025

Fa parte dello standard da Python 2.6. Se dovessi usare una versione precedente, c'è ancora la possibilità di usare la propertyfunzione integrata, che è disponibile da python 2.2
Hatatister

0

In realtà puoi simulare un C#getter e un setter usando questo semplice trucco:

class Screen(object):

    def getter_setter_y(self, y, get=True):
        if get is True:
            Screen.getter_setter_y.value = y
        else:
            return Screen.getter_setter_y.value

     def getter_setter_x(self, x, get=True):
         if get is True:
             Screen.getter_setter_x.value = x
         else:
             return Screen.getter_setter_x.value

Quindi usalo simile come in C#:

scr = Screen()
scr.getter_setter_x(100)
value =  scr.getter_setter_x(0, get=False)
print (value)

Sta semplicemente dichiarando una variabile locale statica in una funzione che svolgerà un ruolo get / set, poiché è l'unico modo per condividere una variabile tramite i metodi get e set, senza renderla globale per una classe o un file.

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.