Pregunta

Puede alguien explicar por qué Python hace lo siguiente?

>>> 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]

Es bastante confuso!

¿Fue útil?

Solución

Como otros han dicho que el código como está escrito crea una variable de clase en lugar de una variable de instancia. Es necesario asignar en __init__ para crear una variable de instancia.

Esperamos que esta copia anotada de su código es útil para explicar lo que está pasando en cada etapa:

>>> 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]

Además, imprimiendo el valor de Foo.bar (la variable de clase accede a través de la clase en lugar de a través de una instancia) después de cada uno de sus estados de cuenta podría ayudar a aclarar lo que está pasando.

Otros consejos

Esto se debe a la forma en que lo ha escrito, bar es una variable de clase en lugar de una variable de instancia.

Para definir una variable de instancia, se unen en el constructor:

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

Tenga en cuenta que ahora pertenece a una sola instancia de Foo (self) en lugar de la clase Foo, y verá los resultados que se pueden esperar cuando se asigna a la misma.

Cuando se declara un elemento de la clase como que es compartida por todas las instancias de la clase. Para hacer un miembro de la clase adecuada que pertenece a cada caso, por separado, crearlo en __init__ como la siguiente:

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

En el principio, bar es una variable de clase y que se comparte entre a y b, tanto a.bar y b.bar se refieren al mismo objeto.

Cuando se asigna un nuevo valor a a.bar, esto no sobrescribe la variable de clase, se añade una nueva variable de instancia al objeto a, ocultando la variable de clase cuando se accede a a.bar. Si a.bar de eliminación (la variable de instancia), y luego resuelve a.bar nuevo a la variable de clase.

b.bar por el contrario siempre se refiere a la variable de clase, que no está influenciada por la bar adicional sobre el objeto a o cualquier valor asignado a esa.

Para establecer la variable de clase se puede acceder a él a través de la propia clase:

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

bar es una variable de clase compartida, no una variable de instancia. Creo que se ocupa de la mayor parte de su confusión. Para que sea un ejemplo var, definirlo en __init__ de clase por las otras respuestas.

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

Esta es la prueba de ello.

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

a.bar Ahora que ha redefinido como una variable de instancia. Eso es lo que sucede cuando se definen variables externamente por defecto.

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

Lo mismo de nuevo. b.bar sigue siendo la variable de clase compartida.

En una nota relacionada, usted debe ser consciente de esta trampa que es posible que aparezca algún momento pronto:

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


a = A()
a2 = A()

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

Esto confunde a mucha gente y tiene que ver con cómo se interpreta el código. Python en realidad interpreta los encabezamientos de las funciones en primer lugar, por lo que evalúa __init__(self, mylist = []) y almacena una referencia a esa lista como parámetro por defecto. Eso significa que todas las instancias de una voluntad (salvo que se disponga su propia lista) hacen referencia a la lista original. El código correcto para hacer tal cosa sería

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

o si desea una expresión más corta puede utilizar la sintaxis ternaria:

self.mylist = mylist if mylist else []
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top