Domanda

Qualcuno può spiegare perché Python fa la seguente?

>>> class Foo(object):
...   bar = []
...
>>> a = Foo()
>>> b = Foo()
>>> a.bar.append(1)
>>> b.bar
[1]
>>> a.bar = 1
>>> a.bar
1
>>> b.bar
[1]
>>> a.bar = []
>>> a.bar
[]
>>> b.bar
[1]
>>> del a.bar
>>> a.bar
[1]

E 'piuttosto confusa!

È stato utile?

Soluzione

Come altri hanno detto il codice come scritto crea una variabile di classe piuttosto che una variabile di istanza. È necessario assegnare in __init__ per creare una variabile di istanza.

Speriamo che questa copia annotata del codice è utile per spiegare quello che sta succedendo in ogni fase:

>>> class Foo(object):
...   bar = []          # defines a class variable on Foo (shared by all instances)
...
>>> a = Foo()
>>> b = Foo()
>>> a.bar.append(1)     # appends the value 1 to the previously empty list Foo.bar
>>> b.bar               # returns the value of the class variable Foo.bar
[1]
>>> a.bar = 1           # binds 1 to the instance variable a.bar, masking the access
>>> a.bar               # you previously had to the class variable through a.bar
1
>>> b.bar               # b doesn't have an instance variable 'bar' so this still
[1]                     # returns the class variable
>>> a.bar = []          # bind a's instance variable to to an empty list
>>> a.bar
[]
>>> b.bar               # b doesn't have an instance variable 'bar' so this still
[1]                     # returns the class variable
>>> del a.bar           # unbinds a's instance variable unmasking the class variable
>>> a.bar               # so a.bar now returns the list with 1 in it.
[1]

Inoltre, stampare il valore della Foo.bar (la variabile di classe accessibile tramite la classe piuttosto che attraverso un esempio) dopo ciascuna delle sue dichiarazioni potrebbero aiutare a chiarire che cosa sta succedendo.

Altri suggerimenti

Questo è perché il modo in cui hai scritto, bar è una variabile di classe piuttosto che una variabile di istanza.

Per definire una variabile di istanza, si legano nel costruttore:

class Foo(object):
  def __init__(self):
    self.bar = []

Si noti che ora appartiene a una singola istanza di Foo (self), piuttosto che la classe Foo, e vedrete i risultati che ci si aspetta quando si assegna ad esso.

Quando si dichiara un elemento nella classe Come che sia condiviso da tutte le istanze della classe. Per effettuare un membro della classe adeguata che appartiene a ciascuna istanza, separatamente, si crea nella __init__ come il seguente:

class Foo(object):
    def __init__(self):
        self.bar = []

In principio, bar è una variabile di classe ed è condivisa tra a e b, sia a.bar e b.bar riferiscono allo stesso oggetto.

Quando si assegna un nuovo valore a a.bar, questo non sovrascrive la variabile di classe, si aggiunge una nuova variabile di istanza per l'oggetto a, nascondendo la variabile di classe quando si accede a.bar. Se a.bar delete (la variabile di istanza), quindi a.bar risolve di nuovo per la variabile di classe.

b.bar invece si riferisce sempre alla variabile di classe, non è influenzato dalla bar supplementare sull'oggetto a o qualsiasi valore assegnato a tale.

Per impostare la variabile di classe è possibile accedervi attraverso la classe stessa:

Foo.bar = 1
>>> class Foo(object):
...   bar = []
...

bar è una variabile di classe condivisa, non una variabile di istanza. Credo che si occupa la maggior parte della vostra confusione. Per rendere un esempio var, definirlo in __init__ di classe per le altre risposte.

>>> a = Foo()
>>> b = Foo()
>>> a.bar.append(1)
>>> b.bar
[1]

Questa è la prova di questo.

>>> a.bar = 1
>>> a.bar
1
>>> b.bar
[1]

a.bar Ora hai ridefinito come variabile di istanza. Ecco cosa succede quando si definiscono le variabili esternamente per impostazione predefinita.

>>> a.bar = []
>>> a.bar
[]
>>> b.bar
[1]
>>> del a.bar
>>> a.bar
[1]

Lo stesso nuovo. b.bar è ancora la variabile di classe condivisa.

In una nota correlata, si dovrebbe essere a conoscenza di questa trappola che si potrebbe vedere a volte presto:

class A:
   def __init__(self, mylist = []):
      self.mylist = mylist


a = A()
a2 = A()

a.mylist.append(3)
print b.mylist #prints [3] ???

Questo confonde un sacco di gente e ha a che fare con come il codice viene interpretato. Pitone effettivamente interpreta le intestazioni delle funzioni prima, quindi valuta __init__(self, mylist = []) e memorizza un riferimento a tale elenco come parametro predefinito. Ciò significa che tutte le istanze di una volontà (a meno che fornite la propria lista) Riferimento l'elenco originale. Il codice corretto per fare una cosa del genere sarebbe

class A:
   def __init__(self, mylist=None):
      if mylist:
         self.mylist = mylist
      else:
         self.mylist = []

o se si vuole un'espressione più breve è possibile utilizzare la sintassi ternario:

self.mylist = mylist if mylist else []
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top