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.
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
@Tom: publique su respuesta como respuesta, no como un comentario. Es muy confuso ver respuestas publicadas como comentarios. –