>>> 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 je le fais A.something = 10
, cela entre A.__dict__
. Quel est ce <attribute '__dict__' of 'A' objects>
trouve dans A.__dict__.__dict__
, et quand elle contient quelque chose?
python
class
metaprogramming
magic-methods
porgarmingduod
la source
la source
ive
. Au moins, cela aurait fait une autreA.__dict__['ive']
question;) Je me verrai dehorsRéponses:
Tout d'abord
A.__dict__.__dict__
est différent deA.__dict__['__dict__']
, et le premier n'existe pas. Ce dernier est l'__dict__
attribut qu'auraient les instances de la classe. C'est un objet descripteur qui renvoie le dictionnaire interne des attributs pour l'instance spécifique. En bref, l'__dict__
attribut d'un objet ne peut pas être stocké dans un objet__dict__
, il est donc accessible via un descripteur défini dans la classe.Pour comprendre cela, vous devez lire la documentation du protocole de descripteur .
La version courte:
A
, l'accès àinstance.__dict__
est fourni parA.__dict__['__dict__']
lequel est identique àvars(A)['__dict__']
.A.__dict__
est fourni partype.__dict__['__dict__']
(en théorie) ce qui est identique àvars(type)['__dict__']
.La version longue:
Les classes et les objets donnent accès aux attributs à la fois via l'opérateur d'attribut (implémenté via la classe ou la métaclasse
__getattribute__
) et l'__dict__
attribut / protocole utilisé parvars(ob)
.Pour les objets normaux, l'
__dict__
objet crée undict
objet séparé , qui stocke les attributs, et__getattribute__
essaie d'abord d'y accéder et d'obtenir les attributs à partir de là (avant d'essayer de rechercher l'attribut dans la classe en utilisant le protocole descripteur, et avant d'appeler__getattr__
). Le__dict__
descripteur de la classe implémente l'accès à ce dictionnaire.x.name
est équivalent à ceux pour essayer:x.__dict__['name']
,type(x).name.__get__(x, type(x))
,type(x).name
x.__dict__
fait de même mais saute le premier pour des raisons évidentesComme il est impossible que le
__dict__
ofinstance
soit stocké dans__dict__
l'instance, il est directement accessible via le protocole de descripteur et est stocké dans un champ spécial de l'instance.Un scénario similaire est vrai pour les classes, bien que leur
__dict__
un objet proxy spécial qui prétend être un dictionnaire (mais peut ne pas l'être en interne), et ne vous permet pas de le modifier ou de le remplacer par un autre. Ce proxy vous permet, entre autres, d'accéder aux attributs d'une classe qui lui sont spécifiques, et non définis dans l'une de ses bases.Par défaut, une
vars(cls)
classe vide porte trois descripteurs -__dict__
pour stocker les attributs des instances,__weakref__
qui sont utilisés en interne parweakref
, et la docstring de la classe. Les deux premiers pourraient avoir disparu si vous définissez__slots__
. Vous n'auriez alors pas d' attributs__dict__
et__weakref__
, mais à la place, vous auriez un attribut de classe unique pour chaque emplacement. Les attributs de l'instance ne seraient alors pas stockés dans un dictionnaire, et leur accès sera fourni par les descripteurs respectifs de la classe.Et enfin, l'incohérence qui
A.__dict__
est différente deA.__dict__['__dict__']
c'est parce que l'attribut__dict__
, par exception, n'est jamais recherchévars(A)
, donc ce qui est vrai car ce n'est pas vrai pour pratiquement tout autre attribut que vous utiliseriez. Par exemple,A.__weakref__
c'est la même chose queA.__dict__['__weakref__']
. Si cette incohérence n'existait pas, l'utilisationA.__dict__
ne fonctionnerait pas et vous devriez toujours utiliser à lavars(A)
place.la source
__dict__
attribut d'un objet ne peut-il pas être stocké dans celui de l'objet__dict__
?__dict__
est censé stocker tous les attributs d'instance, un accès aux attributs du formulaireobj.x
est finalement recherché sur l'objet__dict__
, à savoirobj.__dict__['x']
. Maintenant, s'il__dict__
n'était pas implémenté en tant que descripteur, cela conduirait à une récursivité infinie, car pour y accéder,obj.__dict__
vous devez le rechercher sous la formeobj.__dict__['__dict__']
. Le descripteur contourne ce problème.Puisqu'il
A.__dict__
s'agit d'un dictionnaire stockant desA
attributs,A.__dict__['__dict__']
est la référence directe à ce mêmeA.__dict__
attribut.A.__dict__
contient une (sorte de) référence à lui-même. La partie "kind-of" est la raison pour laquelle l'expressionA.__dict__
renvoie adictproxy
au lieu d'une normaledict
.la source
A.__dict__['__dict__']
n'est pas une référence àA.__dict__
. Il implémente l'__dict__
attribut des instances. Pour essayer cela par vous-même,A.__dict__['__dict__'].__get__(A(), A)
renvoie les attributs deA()
, whileA.__dict__['__dict__'].__get__(A, type)
échoue.Faisons quelques explorations!
Je me demande ce que c'est?
Quels sont les attributs d'un
getset_descriptor
objet?En faisant une copie de cela,
dictproxy
nous pouvons trouver des attributs intéressants, en particulier__objclass__
et__name__
.Donc ,
__objclass__
est une référence àA
et__name__
est juste la chaîne'__dict__'
, le nom d'un attribut peut-être?Voilà, nous l'avons!
A.__dict__['__dict__']
est un objet auquel on peut renvoyerA.__dict__
.la source
__objclass__
c'est la classe qui a défini cet attribut, et non un attribut de cette classe. Cela rend votregetattr
exemple incorrect. Un plus correct seraitgetattr(A().__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__)
KeyError: '__dict__'
, contrairement à celle de @ AndrewClark.Vous pouvez essayer l'exemple simple suivant pour en savoir plus:
D'après l'exemple ci-dessus, il semble que les attributs des objets de classe sont stockés par leur classe, les attributs de classe sont stockés par leur classe, qui sont des métaclasses. Ceci est également validé par:
la source
is
est remplacé==
dans la deuxième comparaison, c'estA.__dict__ is type.__dict__['__dict__'].__get__(A)
-à- dire que le résultat estFalse
à la fois en python 2.7.15+ et 3.6.8.