2011-09-25 13 views
6

Soy nuevo en python y aprendí que los atributos de clase son como miembros de datos estáticos en C++. Sin embargo, me confundí después de probar el siguiente código:python: atributos de clase y atributos de instancia

>>> class Foo: 
...  a=1 
... 
>>> f1=Foo(); 
>>> f2=Foo() 
>>> f1.a 
1 
>>> f1.a=5 
>>> f1.a 
5 
>>> f2.a 
1 

No debería f2.a también igual a 5?

Si a se define como una lista en lugar de un número entero, se espera que el comportamiento:

>>> class Foo: 
...  a=[] 
... 
>>> f1=Foo(); 
>>> f2=Foo() 
>>> f1.a 
[] 
>>> f1.a.append(5) 
>>> f1.a 
[5] 
>>> f2.a 
[5] 

Miré a Python: Difference between class and instance attributes, pero no responde a mi pregunta.

¿Alguien puede explicar por qué la diferencia? Gracias

+0

posible duplicado de [¿Cómo evito tener datos de clase de Python compartidos entre instancias?] (Http: // stackoverflow.com/questions/1680528/how-do-i-avoid-having-python-class-data-shared-among-instances) –

Respuesta

7

No está haciendo lo mismo en su segundo ejemplo. En le primer ejemplo, va a asignar un nuevo valor f1.a:

f1.a = 5 

En el segundo ejemplo, usted está simplemente extendiendo una lista:

f1.a.append(5) 

Esto no cambia lo f1.a está señalando. Si se va en lugar de hacer esto:

f1.a = [5] 

, se verá que este se comporta igual que el primer ejemplo.

Pero considere este ejemplo:

>>> f1=Foo() 
>>> f2=Foo() 
>>> Foo.a = 5 
>>> f1.a 
5 
>>> f2.a 
5 

En este ejemplo, estamos realmente cambiando el valor del atributoclase, y el cambio es visible en todas las instancias de la clase.Cuando Tipo:

f1.a = 5 

Usted está anulando la clase atributo con una instancia atributo .

+2

"Estás anulando el atributo de clase con un atributo de instancia". No lo creo. El atributo de clase permanece intacto. En el código de usuario 11869, el atributo ** a ** se CREA en el namspace de ** f1 ** por la instrucción '' f1.a = 5'', porque este atributo no existía antes, ya que se muestra en mi respuesta con ** \ _ \ _ dict __ ** – eyquem

0

Lo que ocurre es lo siguiente:

Cuando se instancia un nuevo objeto f1 = Foo(), no tiene ningún atributos propios. Cada vez que intente acceder por ejemplo f1.a, te redirige a de la clase Foo.a:

print f1.__dict__ 
{} 

print f1.a 
1 

Sin embargo, si configurarf1.a = 5, la instancia consigue un nuevo atributo de ese valor:

print f1.__dict__ 
{'a': 5} 

La definición de clase no se ve afectada por esto, al igual que cualquier otra instancia.

En el segundo ejemplo, no reasigna nada. Con append solo está usando la misma lista que se definió en la clase. Por lo tanto, su instancia todavía se refiere a esa lista, al igual que todas las demás instancias.

+1

Igual que la respuesta que estaba a punto de escribir. Solo agregaría que la razón por la que "funciona" con una lista es porque en ese caso el objeto es [mutable] (http://docs.python.org/library/stdtypes.html#typesseq-mutable). Como todas las instancias hacen referencia al mismo objeto, todas las modificaciones a ese objeto las ven todas. –

+0

@Debilski: las nuevas instancias a menudo tienen sus propios atributos (el 'f1 .__ dict__' que escribes en tu respuesta es uno de ellos, como se puede ver comparando' id (Foo.a) 'y' id (f1 .a) '). – EOL

5
>>> class Foo: 
...  a=1 
... 
>>> f1=Foo() 
>>> f2=Foo() 
>>> f1.a # no instance attribute here in f1, so look up class attribute in Foo 
1 
>>> f1.a=5 # create new instance attribute in f1 
>>> f1.a # instance attribute here. Great, let's use this. 
5 
>>> f2.a # no instance attribute in f2, look up class attribute in Foo 
1 
>>> 
>>> class Foo: 
...  a=[] 
... 
>>> f1=Foo() 
>>> f2=Foo() 
>>> f1.a   # no instance attribute - look up class attribute in Foo 
[] 
>>> f1.a.append(5) # no instance attribute, modify class attribute in-place 
>>> f1.a   # no instance attribute - look up class attribute in Foo 
[5] 
>>> f2.a   # same here 
[5] 
0

Cuando asigna un atributo en python, no importa dónde ese atributo ya se haya definido, la nueva asignación siempre se aplica al objeto asignado. Cuando diga

>>> f1.a=5 

El objeto que tiene el atributo aquí es la instancia, por lo que es la instancia que obtiene el nuevo valor.

3

Mi consejo: para entender estos casos, hacer pruebas usando id y __dict__ también:

class Foo: 
    a=1 

# Foo.a == 1 
# id(Foo.a) == 10021840 
# Foo.__dict__ == {'a': 1, '__module__': '__main__', '__doc__': None} 


f1 = Foo() 

# f1.a == 1 
# id(f1.a) == 10021840 
# f1.__dict__ == {} 

f2 = Foo() 

# f2.a == 1 
# id(f2.a) == 10021840 
# f2.__dict__ == {} 

f1.a = 5 

# f1.a == 5 
# id(f1.a) == 10021792 
# f1.__dict__ == {'a': 5} 

# f2.a == 1 
# id(f2.a) == 10021840 
# f2.__dict__ == {} 

Esto muestra que mientras la instrucción f1.a = 5 no se ha ejecutado, la instancia f1 no tiene una atributo personal a.

Entonces, ¿por qué la instrucción print f1.a ejecutada antes de f1.a = 5 produce 1?
Esto se debe a:

una instancia de clase tiene un espacio de nombres implementado como un diccionario que es el primer lugar en el que se buscan referencias a atributos. Cuando un atributo no se encuentra allí, y la clase de la instancia tiene un atributo con ese nombre, la búsqueda continúa con los atributos de clase .

http://docs.python.org/reference/datamodel.html#the-standard-type-hierarchy

9

de atributos de clase y atributos de los objetos de Python se almacenan en dictionaries separada. Para el objeto f1, se puede acceder a través de, respectivamente, f1.__class__.__dict__ y f1.__dict__. Ejecutando print f1.__class__ is Foo dará salida a True.

Cuando hace referencia al atributo de un objeto, Python primero intenta buscarlo en el diccionario de objetos. Si no lo encuentra allí, verifica el diccionario de clases (y así sucesivamente hasta la jerarquía de herencia).

Cuando asigna a f1.a, está agregando una entrada al diccionario de objetos para f1. Las búsquedas posteriores de f1.a encontrarán esa entrada. Las búsquedas de f2.a aún encontrarán el atributo de clase: la entrada en el diccionario de atributos de clase.

Puede hacer que el valor de f1.a para volver a 1 eliminándolo:

del f1.a 

Esto eliminará la entrada correspondiente a a en el diccionario de objetos de f1, y las búsquedas posteriores va a continuar en el diccionario de clases . Entonces, después, print f1.a dará salida a 1.

+0

Creo que esta respuesta es más elaborada – Yuncy

+0

@Yuncy, creo que el mismo – eyquem

Cuestiones relacionadas