__dict__ python
¿Cuál es el atributo__dict__.__ dict__ de una clase de Python? (4)
>>> class A(object): pass
...
>>> A.__dict__
<dictproxy object at 0x173ef30>
>>> A.__dict__.__dict__
Traceback (most recent call last):
File "<string>", line 1, in <fragment>
AttributeError: ''dictproxy'' object has no attribute ''__dict__''
>>> A.__dict__.copy()
{''__dict__'': <attribute ''__dict__'' of ''A'' objects> ... }
>>> A.__dict__[''__dict__'']
<attribute ''__dict__'' of ''A'' objects> # What is this object?
Si hago A.something = 10 , esto entra en A.__dict__ . ¿Qué es este <attribute ''__dict__'' of ''A'' objects> encuentra en A.__dict__.__dict__ , y cuándo contiene algo?
Como A.__dict__ es un diccionario que almacena A atributos, A.__dict__[''__dict__''] es la referencia directa a ese mismo atributo A.__dict__ .
A.__dict__ contiene una (especie de) referencia a sí mismo. La parte de "tipo de" es por qué la expresión A.__dict__ devuelve un dictproxy lugar de un dict normal.
>>> class B(object):
... "Documentation of B class"
... pass
...
>>> B.__doc__
''Documentation of B class''
>>> B.__dict__
<dictproxy object at 0x00B83590>
>>> B.__dict__[''__doc__'']
''Documentation of B class''
En primer lugar, A.__dict__.__dict__ es diferente de A.__dict__[''__dict__''] , y el primero no existe. Este último es el atributo __dict__ que __dict__ las instancias de la clase. Es un objeto descriptor que devuelve el diccionario interno de atributos para la instancia específica. En resumen, el atributo __dict__ de un objeto no se puede almacenar en el __dict__ del objeto, por lo que se accede a él a través de un descriptor definido en la clase.
Para entender esto, tendrías que leer la documentación del protocolo de descripción .
La versión corta:
- Para una instancia de clase
A, el acceso a lainstance.__dict__es proporcionado porA.__dict__[''__dict__'']que es lo mismo quevars(A)[''__dict__'']. - Para la clase A, el acceso a
A.__dict__lo proporciona eltype.__dict__[''__dict__''](en teoría) que es lo mismo quevars(type)[''__dict__''].
La versión larga
Tanto las clases como los objetos proporcionan acceso a los atributos a través del operador de atributo (implementado a través de __getattribute__ la clase o metaclase) y del atributo / protocolo __dict__ que vars(ob) usa.
Para objetos normales, el objeto __dict__ crea un objeto dict separado, que almacena los atributos, y __getattribute__ primero intenta acceder a él y obtener los atributos desde allí (antes de intentar buscar el atributo en la clase utilizando el protocolo descriptor, y antes llamando a __getattr__ ). El descriptor __dict__ en la clase implementa el acceso a este diccionario.
-
x.namees equivalente a probarlos en orden:x.__dict__[''name''],type(x).name.__get__(x, type(x)),type(x).name -
x.__dict__hace lo mismo pero se salta el primero por razones obvias
Como es imposible que el __dict__ de la instance se almacene en __dict__ de la instancia, se accede directamente a través del protocolo del descriptor y se almacena en un campo especial en la instancia.
Un escenario similar es cierto para las clases, aunque su __dict__ es un objeto proxy especial que pretende ser un diccionario (pero puede no serlo internamente), y no le permite cambiarlo o reemplazarlo por otro. Este proxy le permite, entre todo lo demás, acceder a los atributos de una clase que son específicos de ella y no están definidos en una de sus bases.
Por defecto, un vars(cls) de una clase vacía lleva tres descriptores: __dict__ para almacenar los atributos de las instancias, __weakref__ que es usado internamente por weakref , y la docstring de la clase. Los primeros dos podrían desaparecer si defines __slots__ . Entonces no __weakref__ atributos __dict__ y __weakref__ , sino que tendrías un único atributo de clase para cada ranura. Los atributos de la instancia no se almacenarán en un diccionario, y los descriptores respectivos de la clase proporcionarán acceso a ellos.
Y, por último, la inconsistencia que A.__dict__ es diferente de A.__dict__[''__dict__''] es porque el atributo __dict__ es, por excepción, nunca se buscó en vars(A) , entonces lo que es verdadero para eso no es verdad para prácticamente cualquier otro atributo que usarías. Por ejemplo, A.__weakref__ es lo mismo que A.__dict__[''__weakref__''] . Si no existiera esta incoherencia, usar A.__dict__ no funcionaría, y en su lugar siempre tendría que usar vars(A) .
Puede probar el siguiente ejemplo simple para comprender más de esto:
>>> class A(object): pass
...
>>> a = A()
>>> type(A)
<type ''type''>
>>> type(a)
<class ''__main__.A''>
>>> type(a.__dict__)
<type ''dict''>
>>> type(A.__dict__)
<type ''dictproxy''>
>>> type(type.__dict__)
<type ''dictproxy''>
>>> type(A.__dict__[''__dict__''])
<type ''getset_descriptor''>
>>> type(type.__dict__[''__dict__''])
<type ''getset_descriptor''>
>>> a.__dict__ == A.__dict__[''__dict__''].__get__(a)
True
>>> A.__dict__ == type.__dict__[''__dict__''].__get__(A)
True
>>> a.__dict__ == type.__dict__[''__dict__''].__get__(A)[''__dict__''].__get__(a)
True
A partir del ejemplo anterior, parece que los atributos de los objetos de clase son almacenados por su clase, los atributos de clase son almacenados por su clase, que son metaclases. Esto también está validado por:
>>> a.__dict__ == A.__getattribute__(a, ''__dict__'')
True
>>> A.__dict__ == type.__getattribute__(A, ''__dict__'')
True
Vamos a explorar!
>>> A.__dict__[''__dict__'']
<attribute ''__dict__'' of ''A'' objects>
Me pregunto qué es eso?
>>> type(A.__dict__[''__dict__''])
<type ''getset_descriptor''>
¿Qué atributos tiene un objeto getset_descriptor ?
>>> type(A.__dict__["__dict__"]).__dict__
<dictproxy object at 0xb7efc4ac>
Al hacer una copia de ese dictproxy podemos encontrar algunos atributos interesantes, específicamente __objclass__ y __name__ .
>>> A.__dict__[''__dict__''].__objclass__, A.__dict__[''__dict__''].__name__
(<class ''__main__.A''>, ''__dict__'')
Así que __objclass__ es una referencia a A y __name__ es solo la cadena ''__dict__'' , tal vez el nombre de un atributo?
>>> getattr(A.__dict__[''__dict__''].__objclass__, A.__dict__[''__dict__''].__name__) == A.__dict__
True
Ahí lo tenemos! A.__dict__[''__dict__''] es un objeto que puede referirse a A.__dict__ .