Una definición de clase aleatoria:

class ABC:
    x = 6

Establecer algunos valores, primero para la instancia abc, luego para la variable estática:

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

Y luego imprima los resultados:

print abc.x
print ABC.x

Que imprime

2
5

Ahora, realmente no entiendo lo que está sucediendo, porque si reemplazo en la definición de clase x = 6 para "pasar", simplemente generará 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 alguien puede establecer en cualquier momento cualquier variable sin hacerlo?

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

13
devoured elysium 8 nov. 2009 a las 20:36

6 respuestas

La mejor respuesta
class SomeClass:
  x=6  # class variable

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

Hay una virtud en declarar una variable de ámbito de clase: sirve como predeterminada para una. Piense en la variable de ámbito de clase como pensaría en las variables "estáticas" en otros idiomas.

12
jldupont 8 nov. 2009 a las 17:40

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

Tomar como 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 puede ver, la instancia de la clase tiene acceso al atributo de la clase que se puede configurar especificando el nombre de la clase y luego el atributo de la clase.

La parte difícil es cuando tienes un atributo de clase y un atributo de instancia llamado igual. Esto requiere una comprensión de lo que está sucediendo debajo del capó.

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

¿Te das cuenta de que la instancia no tiene b como atributo? Arriba, cuando llamamos a inst.b Python realmente busca 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 configura un atributo de instancia con el mismo nombre. Por ejemplo:

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

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

0
Righteous Mullet 11 jun. 2015 a las 23:50

Cada objeto tiene un __dict__. La clase ABC y su instancia, abc, son ambos objetos, por lo que cada uno tiene su propio __dict__ separado:

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

El aviso ABC.__dict__ tiene una tecla 'x':

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

In [5]: abc=ABC()

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

Observe que si 'x' no está en abc.__dict__, entonces se buscan las __dict__ 's de las superclase (s) de abc. Entonces abc.x es "heredado" de ABC:

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

Pero si configuramos 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 lo deseamos:

In [10]: ABC.x = 5

In [11]: ABC.__dict__
Out[11]: {'__doc__': None, '__module__': '__main__', 'x': 5}
3
unutbu 8 nov. 2009 a las 18:13

Python hace una distinción entre los dos. El propósito podría ser múltiple, pero un ejemplo es este:

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 esta instancia puede tomar una ID única al mismo tiempo, que se colocará en self.id. Ambos se almacenan en diferentes lugares: en el objeto de clase o en el objeto de instancia, puede compararlo con las variables estáticas y de instancia en algunos lenguajes OO como C ++ o C #.

En ese ejemplo, si haces:

print token.id

Verá la siguiente ID que se asignará, mientras que:

x = token(10)
print x.id

Le dará la identificación de esa instancia.

Todos también pueden poner otros atributos en una instancia o en una clase, es cierto, pero eso no sería interesante ya que el código de la 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
RedGlyph 8 nov. 2009 a las 17:51

Una variable de nivel de clase (llamada "estática" en otros idiomas) es propiedad de la clase y compartida por 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.

Entonces obtener abc.x requiere primero verificar una variable de instancia. Si no hay una variable de instancia, probará la variable de clase.

Y establecer abc.x creará (o reemplazará) una variable de instancia.

4
S.Lott 8 nov. 2009 a las 18:09

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

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

abc = ABC()

Python crea un nuevo objeto vacío y establece su clase en 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, busca en la clase de la instancia. Luego en las clases base y así sucesivamente. Si nunca encuentra a nadie con el atributo definido, arroja una excepción.

Cuando 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 una referencia al nuevo.

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

abc = ABC()

Solo el objeto ABC (la clase) tiene un atributo llamado x . La instancia abc todavía no tiene su propia x , por lo que si solicita una, 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 los valores que colocas allí.

Ahora debería poder predecir lo que hace este código:

class ABC:
  x = 6

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

Sí: imprime dos cinco. Es posible que haya esperado que arroje una excepción AttributeError. Pero Python encuentra el atributo en la clase, a pesar de que se agregó después de que se creó la instancia.

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

class ABC:
  x = []

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

b = ABC()
print(b.x)

Eso imprimirá [1] . Todas las instancias de ABC () comparten la misma lista. Lo que probablemente querías 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 espera.

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 alguien puede 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 cuestión práctica, esto significa que las instancias no tienen su propia copia del atributo (o al menos todavía no). 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 o no ser lo que 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 cuestión puramente estilística, es un código más corto, ya que no tiene todas esas instancias de self. en 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.

Yo tampoco soy terriblemente consistente. Lo único que estoy seguro de hacer es asignar todos los objetos mutables que no quiero compartir en el método __init__.

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

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 per se. Pero este es un idioma común de Python.

20
Larry Hastings 11 nov. 2009 a las 17:28