python - privados - ¿Cuál es la diferencia entre las variables de clase y de instancia?
poo python 3 (2)
Cuando escribe un bloque de clase, crea atributos de clase (o variables de clase). Todos los nombres que asigna en el bloque de clase, incluidos los métodos que define con def
convierten en atributos de clase.
Después de crear una instancia de clase, todo lo que tenga una referencia a la instancia puede crear atributos de instancia en ella. Dentro de los métodos, la instancia "actual" casi siempre está vinculada al nombre de self
, por lo que estás pensando en estas como "variables propias". Por lo general, en el diseño orientado a objetos, se supone que el código adjunto a una clase tiene control sobre los atributos de las instancias de esa clase, por lo que casi toda la asignación de atributos de instancia se realiza dentro de los métodos, utilizando la referencia a la instancia recibida en el parámetro el método.
Los atributos de clase a menudo se comparan con variables estáticas (o métodos) que se encuentran en lenguajes como Java, C # o C ++. Sin embargo, si desea aspirar a una comprensión más profunda, evitaría pensar en los atributos de clase como "lo mismo" que las variables estáticas. Si bien a menudo se utilizan para los mismos fines, el concepto subyacente es bastante diferente. Más sobre esto en la sección "avanzada" debajo de la línea.
¡Un ejemplo!
class SomeClass:
def __init__(self):
self.foo = ''I am an instance attribute called foo''
self.foo_list = []
bar = ''I am a class attribute called bar''
bar_list = []
Después de ejecutar este bloque, hay una clase SomeClass
, con 3 atributos de clase: __init__
, bar
, y bar_list
.
Entonces crearemos una instancia:
instance = SomeClass()
Cuando esto sucede, se SomeClass
el método __init__
, que recibe la nueva instancia en su parámetro self
. Este método crea dos atributos de instancia: foo
y foo_list
. Entonces esta instancia se asigna a la variable de instance
, por lo que está vinculada a una cosa con esos dos atributos de instancia: foo
y foo_list
.
Pero:
print instance.bar
da:
I am a class attribute called bar
¿Cómo pasó esto? Cuando tratamos de recuperar un atributo a través de la sintaxis de puntos, y el atributo no existe, Python pasa por una serie de pasos para intentar completar su solicitud de todos modos. Lo siguiente que intentará es mirar los atributos de clase de la clase de su instancia. En este caso, encontró una bar
atributos en SomeClass
, por lo que devolvió eso.
Así es también cómo funcionan las llamadas al método por cierto. Cuando llamas a mylist.append(5)
, por ejemplo, mylist
no tiene un atributo llamado append
. Pero la clase de mylist
sí mylist
hace, y está vinculada a un objeto de método. Ese objeto método es devuelto por el bit mylist.append
, y luego el bit (5)
llama al método con el argumento 5
.
La forma en que esto es útil es que todas las instancias de SomeClass
tendrán acceso al mismo atributo de bar
. Podríamos crear un millón de instancias, pero solo necesitamos almacenar esa cadena en la memoria, porque todas pueden encontrarla.
Pero debes ser un poco cuidadoso. Echa un vistazo a las siguientes operaciones:
sc1 = SomeClass()
sc1.foo_list.append(1)
sc1.bar_list.append(2)
sc2 = SomeClass()
sc2.foo_list.append(10)
sc2.bar_list.append(20)
print sc1.foo_list
print sc1.bar_list
print sc2.foo_list
print sc2.bar_list
¿Qué crees que esto imprime?
[1]
[2, 20]
[10]
[2, 20]
Esto se debe a que cada instancia tiene su propia copia de foo_list
, por lo que se adjuntaron por separado. Pero todas las instancias comparten acceso a la misma bar_list
. Entonces, cuando hicimos sc1.bar_list.append(2)
, afectó a sc2
, ¡aunque sc2
aún no existía! Y del mismo modo sc2.bar_list.append(20)
afectó la bar_list
recuperada a través de sc1
. Esto a menudo no es lo que quieres.
Estudio avanzado a continuación. :)
Para asimilar realmente a Python, que proviene de los lenguajes de OO tradicionalmente tipados estáticos como Java y C #, debe aprender a repensar las clases un poco.
En Java, una clase no es realmente una cosa en sí misma. Cuando escribes una clase declaras más cosas que todas las instancias de esa clase tienen en común. En tiempo de ejecución, solo hay instancias (y variables / métodos estáticos, pero en realidad son solo variables y funciones globales en un espacio de nombres asociado a una clase, nada que ver realmente con OO). Las clases son la forma en que escribes en tu código fuente cómo serán las instancias en el tiempo de ejecución; solo "existen" en su código fuente, no en el programa en ejecución.
En Python, una clase no es nada especial. Es un objeto como cualquier otra cosa. Entonces, los "atributos de clase" son exactamente lo mismo que "atributos de instancia"; en realidad, solo hay "atributos". La única razón para establecer una distinción es que tendemos a usar objetos que son clases diferentes de objetos que no son clases. La maquinaria subyacente es todo lo mismo. Es por eso que digo que sería un error pensar en los atributos de clase como variables estáticas de otros idiomas.
Pero lo que realmente hace que las clases de Python sean diferentes de las clases de estilo Java es que, al igual que cualquier otro objeto, cada clase es una instancia de alguna clase .
En Python, la mayoría de las clases son instancias de una clase incorporada llamada type
. Es esta clase la que controla el comportamiento común de las clases, y hace que todas las cosas OO de la manera que lo hace. La forma predeterminada de OO de tener instancias de clases que tienen sus propios atributos y tener métodos / atributos comunes definidos por su clase, es solo un protocolo en Python. Puede cambiar la mayoría de los aspectos si lo desea. Si alguna vez oíste hablar sobre el uso de una metaclase , todo lo que está definiendo es una clase que es una instancia de una clase diferente a la del type
.
Lo único realmente "especial" de las clases (aparte de todas las máquinas integradas para que funcionen de la forma en que lo hacen de manera predeterminada), es la sintaxis del bloque de clases, para que sea más fácil para usted crear instancias de type
. Esta:
class Foo(BaseFoo):
def __init__(self, foo):
self.foo = foo
z = 28
es más o menos equivalente a lo siguiente:
def __init__(self, foo):
self.foo = foo
classdict = {''__init__'': __init__, ''z'': 28 }
Foo = type(''Foo'', (BaseFoo,) classdict)
Y organizará que todos los contenidos de classdict
conviertan en atributos del objeto que se crea.
Entonces, se vuelve casi trivial ver que puedes acceder a un atributo de clase por atributo de Class.attribute
tan fácilmente como i = Class(); i.attribute
i = Class(); i.attribute
. Tanto i
como Class
son objetos y los objetos tienen atributos. Esto también hace que sea fácil entender cómo se puede modificar una clase después de que se ha creado; ¡simplemente asigne sus atributos de la misma manera que lo haría con cualquier otro objeto!
De hecho, las instancias no tienen una relación especial especial con la clase utilizada para crearlas. La forma en que Python sabe qué clase buscar atributos que no se encuentran en la instancia es mediante el atributo oculto __class__
. Que puede leer para descubrir de qué clase es una instancia, al igual que con cualquier otro atributo: c = some_instance.__class__
. Ahora tiene una variable c
ligada a una clase, aunque probablemente no tenga el mismo nombre que la clase. Puedes utilizar esto para acceder a los atributos de clase, o incluso llamarlo para crear más instancias de él (¡aunque no sepas qué clase es!).
¡Y hasta puedes asignar i.__class__
para cambiar la clase de la que es una instancia! Si haces esto, nada en particular ocurre de inmediato. No es estremecedor. Todo lo que significa es que cuando busca atributos que no existen en la instancia, Python buscará los nuevos contenidos de __class__
. Dado que eso incluye la mayoría de los métodos, y los métodos generalmente esperan que la instancia en la que están operando se encuentre en ciertos estados, esto generalmente genera errores si lo hace al azar, y es muy confuso, pero puede hacerse. Si eres muy cuidadoso, lo que almacenas en __class__
ni siquiera tiene que ser un objeto de clase; todo lo que Python va a hacer con él son atributos de búsqueda bajo ciertas circunstancias, así que todo lo que necesita es un objeto que tenga el tipo correcto de atributos (algunas advertencias aparte donde Python se vuelve quisquilloso con las clases o instancias de una clase en particular).
Eso es probablemente suficiente por ahora. Con suerte (si has leído hasta aquí) no te he confundido demasiado. Python está limpio cuando aprende cómo funciona. :)
¿Cuál es la diferencia entre las variables de clase y de instancia en Python?
class Complex:
a = 1
y
class Complex:
def __init__(self):
self.a = 1
Usando la llamada: x = Complex().a
en ambos casos asigna x a 1.
Se __init__()
una respuesta más profunda sobre __init__()
y self
mismo.
Lo que estás llamando una variable de "instancia" no es en realidad una variable de instancia; es una variable de clase . Ver la referencia del lenguaje sobre las clases .
En su ejemplo, el a
parece ser una variable de instancia porque es inmutable. Su naturaleza como variable de clase se puede ver en el caso cuando se asigna un objeto mutable:
>>> class Complex:
>>> a = []
>>>
>>> b = Complex()
>>> c = Complex()
>>>
>>> # What do they look like?
>>> b.a
[]
>>> c.a
[]
>>>
>>> # Change b...
>>> b.a.append(''Hello'')
>>> b.a
[''Hello'']
>>> # What does c look like?
>>> c.a
[''Hello'']
Si usabas self
, entonces sería una verdadera variable de instancia, y así cada instancia tendría su propia y única a
. Se __init__
función __init__
un objeto cuando se crea una nueva instancia , y self
es una referencia a esa instancia.