¿Cuál es la diferencia entre las variables de clase y de instancia?


¿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.

Una respuesta más profunda sobre __init__() y self será apreciada.

Author: martineau, 2012-01-22

2 answers

Cuando escribe un bloque de clase, crea atributos de clase (o variables de clase). Todos los nombres que asigne en el bloque de clase, incluidos los métodos que defina con def se convierten en atributos de clase.

Después de crear una instancia de clase, cualquier cosa con 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 self, por lo que está pensando en estos como"variables propias". Por lo general en 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 métodos, utilizando la referencia a la instancia recibida en el parámetro self del método.

Los atributos de clase se comparan a menudo con las variables (o métodos) estáticas que se encuentran en lenguajes como Java, C# o C++. Sin embargo, si quieres aspirar a una comprensión más profunda, evitaría pensar en 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 "avanzado" 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.

Luego crearemos una instancia:{[59]]}

instance = SomeClass()

Cuando esto sucede, SomeClass se ejecuta el método __init__, recibiendo la nueva instancia en su parámetro self. Este método crea dos atributos de instancia: foo y foo_list. Luego, esta instancia se asigna a la variable 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 sucedió esto? Cuando tratamos de recuperar un atributo a través de la sintaxis de punto, y el atributo no existe, Python pasa por un montón de pasos para tratar de cumplir con su solicitud de todos modos. El lo siguiente que intentará es mirar los atributos de clase de la clase de su instancia. En este caso, encontró un atributo bar en SomeClass, por lo que devolvió eso.

Así es también como funcionan las llamadas de método. Cuando se llama mylist.append(5), por ejemplo, mylist no tiene un atributo llamado append. Pero la clase de mylist sí, y está vinculada a un objeto method. Ese objeto de 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 bar. Podríamos crear un millón de instancias, pero solo necesitamos almacenar esa cadena en la memoria, porque todas pueden encontrarla.

Pero hay que tener un poco de cuidado. Eche 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 imprime esto?

[1]
[2, 20]
[10]
[2, 20]

Esto se debe a que cada instancia tiene su propia copia de foo_list, por lo que fueron anexo a por separado. Pero todas las instancias comparten el acceso a la misma bar_list. Así que cuando lo hicimos sc1.bar_list.append(2) afectó sc2, a pesar de que sc2 aún no existía! Y del mismo modo sc2.bar_list.append(20) afectó al bar_list recuperado a través de sc1. Esto a menudo no es lo que quieres.


El estudio avanzado sigue. :)

Para realmente grok Python, viniendo de lenguajes OO tradicionales tipeados estáticamente como Java y C#, tienes que aprender a repensar un poco las clases.

En Java, a la clase no es realmente una cosa por derecho propio. Cuando escribes una clase estás más declarando un montón de cosas que todas las instancias de esa clase tienen en común. En tiempo de ejecución, solo hay instancias (y métodos/variables estáticos, pero en realidad son solo variables y funciones globales en un espacio de nombres asociado con una clase, nada que ver con OO realmente). Las clases son la forma en que escribe en su código fuente cómo serán las instancias en tiempo de ejecución; solo "existen" en su código fuente código, no en el programa en ejecución.

En Python, una clase no es nada especial. Es un objeto como cualquier otra cosa. Así que " atributos de clase "son de hecho exactamente lo mismo que" atributos de instancia"; en realidad solo hay"atributos". La única razón para hacer una distinción es que tendemos a usar objetos que son clases de manera diferente a objetos que no son clases. La maquinaria subyacente es la misma. Por eso digo que sería un error pensar en la clase atributos como variables estáticas de otros lenguajes.

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 todas las cosas OO de la manera en que lo hace. La forma predeterminada de OO de tener instancias de clases que tienen sus propias attributes, and have common methods/attributes defined by their class, is just a protocol in Python. Puedes cambiar la mayoría de los aspectos si quieres. Si alguna vez has oído hablar del uso de una metaclase , todo lo que es definir una clase que es una instancia de una clase diferente a type.

La única cosa realmente "especial" sobre las clases (aparte de toda la maquinaria incorporada para que funcionen como lo hacen por defecto), es la sintaxis de bloque de clase, para que sea más fácil para usted crear instancias de type. Esto:

class Foo(BaseFoo):
    def __init__(self, foo):
        self.foo = foo

    z = 28

Es aproximadamente equivalente a lo siguiente:

def __init__(self, foo):
    self.foo = foo

classdict = {'__init__': __init__, 'z': 28 }

Foo = type('Foo', (BaseFoo,) classdict)

Y se encargará de que todos los contenidos de classdict se conviertan en atributos del objeto que se crea.

Entonces se vuelve casi trivial ver que se puede acceder a un atributo de clase por Class.attribute tan fácilmente como 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; solo tiene que asignar sus atributos de la misma manera que lo haría con cualquier otro objeto!

De hecho, las instancias no tienen ninguna relación 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 hidden __class__. Que puede leer para averiguar de qué clase es una instancia, al igual que con cualquier otro atributo: c = some_instance.__class__. Ahora tienes una variable c vinculada a una clase, aunque probablemente no tener el mismo nombre que la clase. Puede usar esto para acceder a los atributos de la clase, o incluso llamarlo para crear más instancias de ella (¡aunque no sepa qué clase es!).

E incluso puede asignar a i.__class__ para cambiar de qué clase es una instancia de! Si haces esto, nada en particular sucede inmediatamente. No es devastador. Todo lo que significa es que cuando busque atributos que no existen en la instancia, Python buscará los nuevos contenidos de __class__. Desde que incluye la mayoría de los métodos, y los métodos generalmente esperan que la instancia en la que están operando esté en ciertos estados, esto generalmente resulta en errores si lo hace al azar, y es muy confuso, pero se puede hacer. Si tiene mucho cuidado, lo que almacena en __class__ ni siquiera tiene que ser un objeto de clase; todo lo que Python va a hacer con él es buscar atributos bajo ciertas circunstancias, por lo que todo lo que necesita es un objeto que tenga el tipo correcto de atributos (algunas advertencias aparte donde Python obtiene quisquilloso sobre las cosas que son clases o instancias de una clase en particular).

Eso es probablemente suficiente por ahora. Con suerte (si incluso has leído hasta aquí) no te he confundido demasiado. Python es limpio cuando aprendes cómo funciona. :)

 91
Author: Ben,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-10 23:14:03

Lo que está llamando una variable de "instancia" no es realmente una variable de instancia; es una variable de clase . Consulte la referencia de idioma sobre las clases.

En su ejemplo, a parece ser una variable de instancia porque es inmutable. Su naturaleza como variable class 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 se utiliza self, entonces sería una verdadera variable de instancia, y por lo tanto cada instancia tendría su propia único a. La función __init__ de un objeto se llama cuando se crea una nueva instancia, y self es una referencia a esa instancia.

 10
Author: voithos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-22 05:20:15