Lo que quiero es este comportamiento:
class a:
list = []
x = a()
y = a()
x.list.append(1)
y.list.append(2)
x.list.append(3)
y.list.append(4)
print(x.list) # prints [1, 3]
print(y.list) # prints [2, 4]
Por supuesto, lo que realmente sucede cuando imprimo es:
print(x.list) # prints [1, 2, 3, 4]
print(y.list) # prints [1, 2, 3, 4]
Claramente están compartiendo los datos en clase a
. ¿Cómo obtengo instancias separadas para lograr el comportamiento que deseo?
list
como nombre de atributo.list
es una función integrada para construir una nueva lista. Deberías escribir clases de nombres con mayúscula.Respuestas:
Tu quieres esto:
Declarar las variables dentro de la declaración de clase los convierte en miembros de "clase" y no miembros de instancia. Declararlos dentro del
__init__
método asegura que se cree una nueva instancia de los miembros junto con cada nueva instancia del objeto, que es el comportamiento que está buscando.fuente
x.list = []
, podrías cambiarlo y no afectar a ningún otro. El problema que enfrenta es quex.list
yy.list
son la misma lista, por lo que cuando llama anexar en uno, afecta al otro.__init__
.La respuesta aceptada funciona, pero un poco más de explicación no hace daño.
Los atributos de clase no se convierten en atributos de instancia cuando se crea una instancia. Se convierten en atributos de instancia cuando se les asigna un valor.
En el código original no se asigna ningún valor al
list
atributo después de la instanciación; entonces sigue siendo un atributo de clase. La definición de la lista interna__init__
funciona porque__init__
se llama después de la creación de instancias. Alternativamente, este código también produciría la salida deseada:Sin embargo, el escenario confuso en la pregunta nunca sucederá con objetos inmutables como números y cadenas, porque su valor no se puede cambiar sin asignación. Por ejemplo, un código similar al original con el tipo de atributo de cadena funciona sin ningún problema:
Para resumir: los atributos de clase se convierten en atributos de instancia si y solo si se les asigna un valor después de la instanciación, esté
__init__
o no en el método . Esto es bueno porque de esta manera puede tener atributos estáticos si nunca asigna un valor a un atributo después de la creación de instancias.fuente
Declaró "lista" como una "propiedad de nivel de clase" y no como "propiedad de nivel de instancia". Para que las propiedades tengan un alcance en el nivel de instancia, debe inicializarlas haciendo referencia al parámetro "self" en el
__init__
método (o en otro lugar, según la situación).No tiene que inicializar estrictamente las propiedades de la instancia en el
__init__
método, pero facilita la comprensión.fuente
Aunque la respuesta aceptada es acertada, me gustaría agregar una pequeña descripción.
Hagamos un pequeño ejercicio.
En primer lugar, defina una clase de la siguiente manera:
entonces que tenemos aqui?
temp
que es una cadena__init__
método que estableceself.x
self.temp
Bastante sencillo hasta ahora, ¿sí? Ahora comencemos a jugar con esta clase. Inicialicemos esta clase primero:
Ahora haga lo siguiente:
Bueno,
a.temp
funcionó como se esperaba, pero ¿cómo demoniosA.temp
funcionó? Bueno, funcionó porque temp es un atributo de clase. Todo en Python es un objeto. Aquí A es también un objeto de clasetype
. Por lo tanto, el atributo temp es un atributo mantenido por laA
clase y si cambia el valor de temp a través deA
(y no a través de una instancia dea
), el valor cambiado se reflejará en toda la instancia deA
clase. Avancemos y hagamos eso:¿Interesante no? Y tenga en cuenta que
id(a.temp)
yid(A.temp)
siguen siendo los mismos .Cualquier objeto de Python recibe automáticamente un
__dict__
atributo, que contiene su lista de atributos. Investiguemos qué contiene este diccionario para nuestros objetos de ejemplo:Tenga en cuenta que el
temp
atributo se enumera entreA
los atributos de la clase mientras quex
se enumera para la instancia.Entonces, ¿cómo es que obtenemos un valor definido de
a.temp
si ni siquiera está listado para la instanciaa
? Bueno, esa es la magia del__getattribute__()
método. En Python, la sintaxis punteada invoca automáticamente este método, por lo que cuando escribimosa.temp
, Python se ejecutaa.__getattribute__('temp')
. Ese método realiza la acción de búsqueda de atributos, es decir, encuentra el valor del atributo buscando en diferentes lugares.La implementación estándar de
__getattribute__()
búsquedas primero el diccionario interno ( dict ) de un objeto, luego el tipo del objeto en sí. En este caso sea.__getattribute__('temp')
ejecuta primeroa.__dict__['temp']
y luegoa.__class__.__dict__['temp']
Bien, ahora usemos nuestro
change
método:Bueno, ahora que lo hemos usado
self
,print(a.temp)
nos da un valor diferente deprint(A.temp)
.Ahora, si comparamos
id(a.temp)
yid(A.temp)
, serán diferentes.fuente
Sí, debe declarar en el "constructor" si desea que la lista se convierta en una propiedad de objeto y no en una propiedad de clase.
fuente
Así que casi todas las respuestas aquí parecen perder un punto en particular. Las variables de clase nunca se convierten en variables de instancia, como lo demuestra el siguiente código. Al utilizar una metaclase para interceptar la asignación de variables a nivel de clase, podemos ver que cuando se reasigna a.myattr, no se llama al método mágico de asignación de campo en la clase. Esto se debe a que la asignación crea una nueva variable de instancia . Este comportamiento no tiene absolutamente nada que ver con la variable de clase, como lo demuestra la segunda clase que no tiene variables de clase y, sin embargo, todavía permite la asignación de campo.
EN CORTO Las variables de clase no tienen NADA que ver con las variables de instancia.
Más claramente , simplemente están dentro del alcance de las búsquedas en instancias. Las variables de clase son, de hecho , variables de instancia en el propio objeto de clase. También puede tener variables de metaclases si lo desea, porque las metaclases en sí mismas también son objetos. Todo es un objeto, ya sea que se use para crear otros objetos o no, así que no se enrede en la semántica del uso de la clase de palabras en otros idiomas. En python, una clase es realmente solo un objeto que se usa para determinar cómo crear otros objetos y cuáles serán sus comportamientos. Las metaclases son clases que crean clases, solo para ilustrar más este punto.
fuente
Para proteger su variable compartida por otra instancia, debe crear una nueva variable de instancia cada vez que cree una instancia. Cuando declara una variable dentro de una clase, es variable de clase y es compartida por todas las instancias. Si desea hacerlo, por ejemplo, debe usar el método init para reinicializar la variable como se refiere a la instancia
Desde Python Objects and Class por Programiz.com :
Por ejemplo:
fuente