>>> 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 lo hago A.something = 10, esto entra en A.__dict__. ¿Qué es esto <attribute '__dict__' of 'A' objects>que se encuentran en A.__dict__.__dict__, y cuándo se contiene algo?
python
class
metaprogramming
magic-methods
porgarmingduod
fuente
fuente

ive. Al menos habría hecho de esta unaA.__dict__['ive']pregunta más ;) MeRespuestas:
Primero que nada
A.__dict__.__dict__es diferenteA.__dict__['__dict__']y el primero no existe. Este último es el__dict__atributo que tendrían las instancias de la clase. Es un objeto descriptor que devuelve el diccionario interno de atributos para la instancia específica. En resumen, el__dict__atributo de un objeto no se puede almacenar en el objeto__dict__, por lo que se accede a través de un descriptor definido en la clase.Para entender esto, tendría que leer la documentación del protocolo descriptor .
La versión corta:
A, el acceso ainstance.__dict__es proporcionado por loA.__dict__['__dict__']que es lo mismo quevars(A)['__dict__'].A.__dict__lo proporcionatype.__dict__['__dict__'](en teoría), que es el mismo quevars(type)['__dict__'].La versión larga:
Tanto las clases como los objetos proporcionan acceso a los atributos tanto a través del operador de atributo (implementado a través de la clase o metaclase
__getattribute__) como del__dict__atributo / protocolo que utilizavars(ob).Para los objetos normales, el
__dict__objeto crea undictobjeto 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 de llamar__getattr__). El__dict__descriptor de la clase implementa el acceso a este diccionario.x.namees equivalente a tratar aquellos en orden:x.__dict__['name'],type(x).name.__get__(x, type(x)),type(x).namex.__dict__hace lo mismo pero omite el primero por razones obviasComo es imposible que el
__dict__deinstancese almacene en__dict__la instancia, se accede a él directamente a través del protocolo descriptor y se almacena en un campo especial en la instancia.Un escenario similar es cierto para las clases, aunque
__dict__hay un objeto proxy especial que pretende ser un diccionario (pero puede que no lo sea internamente) y no le permite cambiarlo o reemplazarlo por otro. Este proxy te permite, entre todo lo demás, acceder a los atributos de una clase que le son específicos y no definidos en una de sus bases.De forma predeterminada, una
vars(cls)de una clase vacía lleva tres descriptores:__dict__para almacenar los atributos de las instancias,__weakref__que se usa internamenteweakref, y la cadena de documentos de la clase. Los dos primeros podrían desaparecer si lo define__slots__. Entonces no tendrías atributos__dict__y__weakref__, sino que tendrías un atributo de clase único para cada espacio. Los atributos de la instancia entonces no se almacenarían en un diccionario, y el acceso a ellos será proporcionado por los descriptores respectivos en la clase.Y, por último, la inconsistencia que
A.__dict__es diferenteA.__dict__['__dict__']es porque el atributo__dict__, por excepción, nunca se buscavars(A), por lo que lo que es cierto no es cierto para prácticamente cualquier otro atributo que usaría. Por ejemplo,A.__weakref__es lo mismo queA.__dict__['__weakref__']. Si esta inconsistencia no existiera, el usoA.__dict__no funcionaría y tendría que usar siempre en suvars(A)lugar.fuente
__dict__puede almacenar el atributo de un objeto en el del objeto__dict__?__dict__está destinado a almacenar todos los atributos de instancia, un acceso de atributo del formularioobj.xfinalmente se busca en el objeto__dict__, a saberobj.__dict__['x']. Ahora bien, si__dict__no se implementó como un descriptor, esto conduciría a una recursión infinita, ya que para accederobj.__dict__necesitaría buscarlo comoobj.__dict__['__dict__']. El descriptor evita este problema.Dado que
A.__dict__es un diccionario que almacenaAatributos,A.__dict__['__dict__']es la referencia directa a ese mismoA.__dict__atributo.A.__dict__contiene una (especie de) referencia a sí mismo. La parte "tipo de" es la razón por la que la expresiónA.__dict__devuelve un endictproxylugar de un normaldict.>>> 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'fuente
A.__dict__['__dict__']no es una referencia aA.__dict__. Implementa el__dict__atributo de las instancias. Para probar esto por sí mismo,A.__dict__['__dict__'].__get__(A(), A)devuelve los atributos deA(), mientrasA.__dict__['__dict__'].__get__(A, type)falla.¡Exploremos un poco!
>>> A.__dict__['__dict__'] <attribute '__dict__' of 'A' objects>Me pregunto que es eso
>>> type(A.__dict__['__dict__']) <type 'getset_descriptor'>¿Qué atributos tiene un
getset_descriptorobjeto?>>> type(A.__dict__["__dict__"]).__dict__ <dictproxy object at 0xb7efc4ac>Al hacer una copia de eso
dictproxypodemos encontrar algunos atributos interesantes, específicamente__objclass__y__name__.>>> A.__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__ (<class '__main__.A'>, '__dict__')Entonces, ¿
__objclass__es una referenciaAy__name__es solo la cadena'__dict__', quizás 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 al que se puede hacer referenciaA.__dict__.fuente
__objclass__es la clase que definió este atributo, no que sea un atributo de esa clase. Esto hace que sugetattrejemplo sea incorrecto. Uno más correcto seríagetattr(A().__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__)KeyError: '__dict__', contrariamente a la de @ AndrewClark.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) TrueA partir del ejemplo anterior, parece que los atributos de los objetos de clase son almacenados por su clase, los atributos de la 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__') Truefuente
isse sustituye==en la segunda comparación, es decirA.__dict__ is type.__dict__['__dict__'].__get__(A), el resultado estáFalseen python 2.7.15+ y 3.6.8.