2010-03-11 15 views
5

He estado tratando de entender el manejo de Python de las variables de clase y de instancia. En particular, encontré this answer bastante útil. Básicamente dice que si declaras una variable de clase y luego haces una asignación a [instance].property, estarás asignando a una variable completamente diferente, una en un espacio de nombre diferente de la variable de clase.Asignación compuesta a la clase Python y variables de instancia

Así entonces considerado - si quiero todas las instancias de mi clase para tener un miembro con algún valor por defecto (decir cero), debería hacerlo de esta manera:

class Foo: 
    num = 0 

o así?

class Foo: 
    def __init__(self): 
     self.num = 0 

Sobre la base de lo que había leído antes, pensaría que el segundo ejemplo sería la inicialización de la variable 'derecho' (la instancia en lugar de la variable de clase). Sin embargo, creo que el primer método funciona perfectamente también:

class Foo: 
    num = 0 

bar = Foo() 
bar.num += 1 # good, no error here, meaning that bar has an attribute 'num' 
bar.num 
>>> 1 
Foo.num 
>>> 0 # yet the class variable is not modified! so what 'num' did I add to just now? 

Así que ... ¿por qué funciona esto? ¿Qué no estoy recibiendo? FWIW, mi conocimiento previo de OOP proviene de C++, por lo que la explicación por analogía (o señalando dónde se descompone) podría ser útil.

Respuesta

5

Personalmente, he encontrado these documents por Shalabh Chaturvedi extremadamente útil e informativo con respecto a este tema.

es bar.num = bar.num + 1. Esto está recogiendo la variable de clase Foo.num en el lado derecho y asignándola a una variable de instancia bar.num.

+1

Y cuando digo clase/instancia 'variable', lo que realmente quiero decir es' atributo'. – MattH

0
bar.num += 1 

crea una nueva variable de instancia 'num' en la instancia de 'bar', ya que aún no existe (y luego añade 1 a este valor)

un ejemplo:

class Foo: 
    def __init__(self): 
    self.num= 1 

bar = Foo() 
print bar.num 

esta opción se imprime 1

print bar.foo 

esto da un error como se esperaba: Rastreo (llamada más reciente pasado): Archivo "", línea 1, en AttributeError: instancia de Foo no tiene atributo 'foo'

bar.foo = 5 
print bar.foo 

ahora esto imprime 5

así que lo que sucede en su ejemplo: bar.num se resuelve como Foo. num porque solo hay un atributo de clase. entonces foo.num en realidad se crea porque le asigna un valor.

+0

uh ... 'bar.foo + = 1' también da un error, así que claramente Python no crea variables de instancia cada vez que las encuentra en un expresión que contiene '+ ='. Mi pregunta es, ¿cómo la creación de variables de clase conduce a la inicialización de las variables de instancia correspondientes? – int3

+1

@ int3. Esto se refiere a cómo se resuelven los atributos. Cuando se accede a un atributo, el objeto se busca primero ('num' en este caso), luego el tipo del objeto (' Foo') en este caso, luego las clases base del tipo del objeto y sus bases. Si no se encuentra ningún atributo coincidente, se genera 'AttributeError'. – MattH

+0

de hecho, y luego la variable de instancia se crea porque le asignamos un valor. Actualizaré el ejemplo –

-3

Creo que acabas de encontrar un error en Python. bar.num + = 1 debería ser un error; en su lugar, está creando un atributo numérico en la barra de objetos, distinto de Foo.num.

Es un comportamiento realmente extraño.

+0

@Morgaelyn: No, esto no es un error, este es el comportamiento previsto. Lea en las clases de Python http://docs.python.org/tutorial/classes.html – MattH

+0

Como dice MattH, este comportamiento intencionado y documentado. Entonces, no, no es un error. – krawyoti

+0

Ahora lo he entendido. b.num + = 1 es lo mismo que b.num = b.num + 1. El primer b.num se refiere al atributo del objeto y se crea. El segundo b.num se refiere en realidad a Foo.num y es 0. AHORA eso tiene sentido. Sigo defendiendo que este es un error de diseño en el lenguaje. Es fácil suponer que b.num en b.num + = 1 se refiere siempre a la misma variable, pero en realidad se refiere a dos variables no relacionadas al mismo tiempo. –

2

En el siguiente código, num es miembro de la clase.

class Foo: 
    num = 0 

equivalente A C++ sería algo así como

struct Foo { 
    static int num; 
}; 

int Foo::num = 1; 

class Foo: 
    def __init__(self): 
     self.num = 0 

self.num es un miembro de instancia (self ser una instancia de Foo).

En C++, sería algo así como

struct Foo { 
    int num; 
}; 

creo que Python le permite tener un miembro de la clase y un miembro de instancia que comparten el mismo nombre (C++ no lo hace). Por lo tanto, cuando lo hace bar = Foo(), bar es una instancia de Foo, por lo que con bar.num += 1, incrementa el miembro de la instancia.

+0

¡Su equivalente en C++ parece haber incrementado el valor de num en 1! :) – Kylotan

0

Buscando esta misma pregunta, tanto esta pregunta StackOverflow y dos diapositivas (bastante antiguas, pero válidas) de Guido van Rossum (1, 2) subieron. Las diapositivas de Guido indican que este comportamiento tiene que ver con el orden de búsqueda de Python para acceder al atributo (en el caso del ejemplo anterior num). Pensé que sería bueno poner los dos juntos aquí :)

Cuestiones relacionadas