2009-11-08 11 views
10

Una definición aleatoria de clase:Diferencias entre variables estáticas y de instancia en python. ¿Incluso existen?

class ABC: 
    x = 6 

Ajuste de algunos valores, primero para la instancia ABC, después de la variable estática:

abc = ABC() 
abc.x = 2 
ABC.x = 5 

y luego imprimir los resultados:

print abc.x 
print ABC.x 

que imprime

2 
5 

Ahora, realmente no entiendo lo que está sucediendo, porque si lo reemplazo en la definición de clase x = 6 por "pase", simplemente dará como resultado lo mismo. Mi pregunta es, ¿cuál es el propósito de definir una variable en la definición de clase en Python si parece que puedo establecer en cualquier momento cualquier variable sin hacerlo?

Además, ¿python sabe la diferencia entre la instancia y las variables estáticas? Por lo que vi, yo diría que sí.

+2

Creo que parte de la razón de su confusión es que todas las variables en la clase ABC (y la instancia abc) son "públicas". También se crean dinámicamente. entonces "abc.x = 2" en realidad crea una nueva variable miembro no estática en la instancia abc ... Es decir ... "abc.x = 2" funcionaría si no hubiera una variable estática x. Eso podría ser más claro si prueba abc.y = 2. – Tom

+2

@Tom: publique su respuesta como respuesta, no como un comentario. Es muy confuso ver respuestas publicadas como comentarios. –

Respuesta

11
class SomeClass: 
    x=6 # class variable 

    def __init__(self): 
    self.y = 666 # instance variable 

Es útil declarar una variable de ámbito de clase: sirve como valor predeterminado para uno. Piense en la variable de ámbito de clases como podría pensar en variables "estáticas" en algunos otros idiomas.

3

Una variable de nivel de clase (llamada "estática" en otros idiomas) es propiedad de la clase y la comparten todas las instancias de la clase.

Una variable de instancia es parte de cada instancia distinta de la clase.

Sin embargo.

Puede agregar una nueva variable de instancia en cualquier momento que desee.

Para obtener abc.x primero se debe buscar una variable de instancia. Si no hay una variable de instancia, probará la variable de clase.

Y la configuración abc.x creará (o reemplazará) una variable de instancia.

2

Python hace una distinción entre los dos. El objetivo podría ser múltiples, pero un ejemplo es la siguiente:

class token(object): 
    id = 0 

    def __init__(self, value): 
     self.value = value 
     self.id = token.id 
     token.id += 1 

Aquí, la variable de clase token.id se incrementa automáticamente en cada nueva instancia, y este caso puede tener un identificador único, al mismo tiempo, que se pondrán en self.id. Ambos se almacenan en diferentes lugares: en el objeto de clase o en el objeto de instancia, puede comparar eso con variables estáticas y de instancia en algunos lenguajes de OO como C++ o C#.

En ese ejemplo, si lo hace:

print token.id 

verá el siguiente ID que se asignará, en tanto que:

x = token(10) 
print x.id 

dará el id de esa instancia.

Cualquiera también puede poner otros atributos en una instancia o en una clase, eso es correcto, pero eso no sería interesante ya que el código de clase no está destinado a usarlos. El interés con un ejemplo como el anterior es que el código de clase los usa.

3

Cada objeto tiene un __dict__. La clase ABC y su ejemplo, abc, son los dos objetos, por lo que cada uno tiene su propia separada __dict__:

In [3]: class ABC: 
    ...:  x=6 

Aviso ABC.__dict__ tiene una 'x' clave:

In [4]: ABC.__dict__ 
Out[4]: {'__doc__': None, '__module__': '__main__', 'x': 6} 

In [5]: abc=ABC() 

In [6]: abc.__dict__ 
Out[6]: {} 

Tenga en cuenta que si 'x 'no está en abc.__dict__, entonces se buscan las __dict__ de la (s) superclase (s) de abc. Así abc.x es "heredado" de ABC:

In [14]: abc.x 
Out[14]: 6 

Pero si fijamos abc.x entonces estamos cambiando abc.__dict__, no ABC.__dict__:

In [7]: abc.x = 2 

In [8]: abc.__dict__ 
Out[8]: {'x': 2} 

In [9]: ABC.__dict__ 
Out[9]: {'__doc__': None, '__module__': '__main__', 'x': 6} 

Por supuesto, podemos cambiar ABC.__dict__ si deseamos:

In [10]: ABC.x = 5 

In [11]: ABC.__dict__ 
Out[11]: {'__doc__': None, '__module__': '__main__', 'x': 5} 
16

Advertencia: la siguiente es una simplificación excesiva; Estoy ignorando __new__() y un montón de otros métodos de clases especiales, y muchos detalles. Pero esta explicación te llevará bastante lejos en Python.

Cuando se crea una instancia de una clase en Python, como llamar ABC() en su ejemplo:

abc = ABC() 

Python crea un nuevo objeto vacío y se define la clase a ABC. Luego llama al __init__() si hay uno. Finalmente devuelve el objeto.

Cuando solicita un atributo de un objeto, primero se ve en la instancia. Si no lo encuentra, se ve en la clase de la instancia. Luego en la (s) clase (s) base y demás. Si nunca encuentra a nadie con el atributo definido, arroja una excepción.

Cuando se asigna a un atributo de un objeto, crea ese atributo si el objeto aún no tiene uno. Luego establece el atributo a ese valor. Si el objeto ya tenía un atributo con ese nombre, descarta la referencia al valor anterior y toma como referencia el nuevo.

Estas reglas hacen que el comportamiento que observa sea fácil de predecir. Después de esta línea:

abc = ABC() 

sólo el ABC objeto (la clase) tiene un atributo llamado x . La instancia de abc no tiene su propia x, por lo que si la solicita obtendrá el valor de ABC.x. Pero luego reasigna el atributo x tanto en la clase como en el objeto.Y cuando posteriormente examinas esos atributos, observas que los valores que colocas allí siguen ahí.

Ahora usted debería ser capaz de predecir lo que hace este código:

class ABC: 
    x = 6 

a = ABC() 
ABC.xyz = 5 
print(ABC.xyz, a.xyz) 

Sí: Imprime dos cincos. Es posible que haya esperado lanzar una excepción AttributeError. Pero Python encuentra el atributo en la clase, aunque se haya agregado después de, se creó la instancia.

Este comportamiento realmente puede meterlo en problemas. Un error clásico principiante en Python:

class ABC: 
    x = [] 

a = ABC() 
a.x.append(1) 

b = ABC() 
print(b.x) 

que imprimirá [1]. Todas las instancias de ABC() comparten la misma lista. Lo que probablemente quería era esto:

class ABC: 
    def __init__(self): 
    self.x = [] 

a = ABC() 
a.x.append(1) 

b = ABC() 
print(b.x) 

Eso imprimirá una lista vacía como esperaba.

para responder a sus preguntas exactas:

Mi pregunta es, ¿cuál es el propósito de definir una variable en la definición de clase en Python si parece que puedo cualquier establecer en cualquier momento cualquier variable sin hacerlo ?

Supongo que esto significa "¿por qué debería asignar miembros dentro de la clase, en lugar de dentro del método __init__?"

Como una cuestión práctica, esto significa que las instancias no tienen su propia copia del atributo (o al menos no todavía). Esto significa que las instancias son más pequeñas; también significa que el acceso al atributo es más lento. También significa que todas las instancias comparten el mismo valor para ese atributo, que en el caso de objetos mutables puede ser o no lo que usted desea. Finalmente, las asignaciones aquí significan que el valor es un atributo de la clase, y esa es la forma más directa de establecer atributos en la clase.

Como una cuestión puramente estilística es un código más corto, ya que no tiene todas las instancias de auto. por todas partes. Más allá de eso, no hace mucha diferencia. Sin embargo, la asignación de atributos en el método __init__ garantiza que sean variables de instancia inequívocamente.

No soy muy consistente conmigo mismo. Lo único que estoy seguro es asignar todos los objetos mutables que no quiero compartir en el método __init__.

Además, ¿sabe python la diferencia entre la instancia y las variables estáticas? Por lo que vi, yo diría que sí.

Las clases de Python no tienen variables estáticas de clase como C++. Solo hay atributos: atributos del objeto de clase y atributos del objeto de instancia. Y si solicita un atributo y la instancia no lo tiene, obtendrá el atributo de la clase.

La aproximación más cercana de una variable estática de clase en Python sería un atributo de módulo oculto, así:

_x = 3 
class ABC: 
    def method(self): 
    global _x 
    # ... 

No es parte de la clase en sí.Pero esta es una expresión común de Python.

0

El beneficio de un "atributo de clase" estático o en Python es que cada instancia de la clase tendrá acceso al mismo atributo de clase. Esto no es cierto para los atributos de instancia, ya que puede estar al tanto.

Tomemos, por ejemplo:

class A(object): 
    b = 1 

A.b  # => 1 
inst = A() 
inst2 = A() 
inst.b  # => 1 
inst2.b # => 1 
A.b = 5 
inst.b  # => 5 
inst2.b # => 5 

Como se puede ver la instancia de la clase tiene acceso al atributo de clase que se puede establecer mediante la especificación del nombre de la clase y luego el atributo de clase.

La parte difícil es cuando tienes un atributo de clase y un atributo de instancia con el mismo nombre. Esto requiere una comprensión de lo que sucede bajo el capó.

inst.__dict__ # => {} 
A.__dict__  # => {..., 'b': 5} 

Note como la instancia no tiene b como un atributo? Más arriba, cuando llamamos al inst.b Python realmente comprueba inst.__dict__ para el atributo, si no se puede encontrar, entonces busca A.__dict__ (los atributos de la clase). Por supuesto, cuando Python busca b en los atributos de la clase, se encuentra y se devuelve.

Puede obtener resultados confusos si establece un atributo de instancia con el mismo nombre. Por ejemplo:

inst.b = 10 
inst.__dict__ #=> {'b': 10} 
A.b   # => 5 
inst.b  # => 10 

Se puede ver que la instancia de la clase ahora tiene el atributo b instancia y, por tanto, Python devuelve ese valor.

Cuestiones relacionadas