>>> 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?
Wenn ich das tue A.something = 10
, geht das in A.__dict__
. Was ist diese <attribute '__dict__' of 'A' objects>
gefunden A.__dict__.__dict__
, und wann wird es etwas enthalten?
python
class
metaprogramming
magic-methods
porgarmingduod
quelle
quelle
ive
. Zumindest hätte es dies zu einer weiterenA.__dict__['ive']
Frage gemacht;) Ich werde mich selbst sehenAntworten:
Erstens
A.__dict__.__dict__
ist anders alsA.__dict__['__dict__']
, und die erstere existiert nicht. Letzteres ist das__dict__
Attribut, das die Instanzen der Klasse haben würden. Es ist ein Deskriptorobjekt, das das interne Wörterbuch der Attribute für die bestimmte Instanz zurückgibt. Kurz gesagt, das__dict__
Attribut eines Objekts kann nicht in einem Objekt gespeichert werden__dict__
, daher wird auf es über einen in der Klasse definierten Deskriptor zugegriffen.Um dies zu verstehen, müssen Sie die Dokumentation des Deskriptorprotokolls lesen .
Die Kurzversion:
A
wird der Zugriff aufinstance.__dict__
bereitgestelltA.__dict__['__dict__']
, der mit identisch istvars(A)['__dict__']
.A.__dict__
durch bereitgestellt,type.__dict__['__dict__']
was derselbe ist wievars(type)['__dict__']
.Die lange Version:
Sowohl Klassen als auch Objekte bieten Zugriff auf Attribute sowohl über den Attributoperator (implementiert über die Klasse oder die Metaklasse
__getattribute__
) als auch über das__dict__
Attribut / Protokoll, das von verwendet wirdvars(ob)
.Bei normalen Objekten erstellt das
__dict__
Objekt ein separatesdict
Objekt, in dem die Attribute gespeichert werden, und__getattribute__
versucht zunächst, darauf zuzugreifen und die Attribute von dort abzurufen (bevor versucht wird, das Attribut in der Klasse mithilfe des Deskriptorprotokolls zu suchen, und bevor es aufgerufen wird__getattr__
). Der__dict__
Deskriptor für die Klasse implementiert den Zugriff auf dieses Wörterbuch.x.name
ist äquivalent zu denen , um versuchen:x.__dict__['name']
,type(x).name.__get__(x, type(x))
,type(x).name
x.__dict__
macht das gleiche, überspringt aber den ersten aus offensichtlichen GründenDa es unmöglich ist, das
__dict__
ofinstance
in__dict__
der Instanz zu speichern , wird stattdessen direkt über das Deskriptorprotokoll darauf zugegriffen und in einem speziellen Feld in der Instanz gespeichert.Ein ähnliches Szenario gilt für Klassen, obwohl
__dict__
es sich um ein spezielles Proxy-Objekt handelt, das sich als Wörterbuch ausgibt (möglicherweise jedoch nicht intern) und es Ihnen nicht ermöglicht, es zu ändern oder durch ein anderes zu ersetzen. Mit diesem Proxy können Sie unter anderem auf die Attribute einer Klasse zugreifen, die spezifisch für sie sind und nicht in einer ihrer Basen definiert sind.Standardmäßig enthält a
vars(cls)
einer leeren Klasse drei Deskriptoren -__dict__
zum Speichern der Attribute der Instanzen,__weakref__
die intern von verwendet werdenweakref
, und der Dokumentzeichenfolge der Klasse. Die ersten beiden könnten weg sein, wenn Sie definieren__slots__
. Dann würden Sie nicht haben__dict__
und__weakref__
Attribute, sondern würden Sie für jeden Steckplatz eine einzige Klasse Attribut haben. Die Attribute der Instanz würden dann nicht in einem Wörterbuch gespeichert, und der Zugriff darauf wird von den jeweiligen Deskriptoren in der Klasse bereitgestellt.Und schließlich, die Inkonsistenz , die
A.__dict__
von dem unterscheidet,A.__dict__['__dict__']
ist , da das Attribut__dict__
ist, ausnahmsweise nie in nachgeschlagenvars(A)
, so was für sie richtig ist für praktisch jedes andere Attribut nicht wahr ist , dass Sie verwenden würde. Zum BeispielA.__weakref__
ist das gleiche wieA.__dict__['__weakref__']
. Wenn diese Inkonsistenz nicht vorhandenA.__dict__
wäre, würde die Verwendung nicht funktionieren, und Sie müsstenvars(A)
stattdessen immer verwenden .quelle
__dict__
Attribut eines Objekts nicht genau im Objekt gespeichert werden__dict__
?__dict__
alle Instanzattribute gespeichert werden sollen,obj.x
wird schließlich ein Attributzugriff des Formulars auf dem Objekt nachgeschlagen__dict__
, nämlichobj.__dict__['x']
. Wenn dies__dict__
nicht als Deskriptor implementiert wäre, würde dies zu einer unendlichen Rekursion führen, daobj.__dict__
Sie für den Zugriff nachschlagen müsstenobj.__dict__['__dict__']
. Der Deskriptor umgeht dieses Problem.Da
A.__dict__
ein WörterbuchA
Attribute speichert ,A.__dict__['__dict__']
ist der direkte Verweis auf dasselbeA.__dict__
Attribut.A.__dict__
enthält einen (Art) Verweis auf sich selbst. Der "Art-of" -Teil ist, warum der AusdruckA.__dict__
eindictproxy
anstelle eines normalen zurückgibtdict
.quelle
A.__dict__['__dict__']
ist kein Hinweis aufA.__dict__
. Es implementiert das__dict__
Attribut der Instanzen. Um dies selbst zu versuchen, werdenA.__dict__['__dict__'].__get__(A(), A)
die Attribute von zurückgegebenA()
, während diesA.__dict__['__dict__'].__get__(A, type)
fehlschlägt.Lass uns etwas erkunden!
Ich frage mich, was das ist?
Welche Attribute hat ein
getset_descriptor
Objekt?Wenn wir eine Kopie davon
dictproxy
erstellen, können wir einige interessante Attribute finden, insbesondere__objclass__
und__name__
.Ist
__objclass__
also ein Verweis aufA
und__name__
nur die Zeichenfolge'__dict__'
, vielleicht der Name eines Attributs?Da haben wir es!
A.__dict__['__dict__']
ist ein Objekt, auf das zurückgegriffen werden kannA.__dict__
.quelle
__objclass__
ist dies die Klasse, die dieses Attribut definiert hat, nicht das Attribut dieser Klasse. Dies macht Ihrgetattr
Beispiel falsch. Eine korrektere wäregetattr(A().__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__)
KeyError: '__dict__'
wird im Gegensatz zu @ AndrewClark erhöht.Sie können das folgende einfache Beispiel ausprobieren, um mehr davon zu verstehen:
Aus dem obigen Beispiel geht hervor, dass Klassenobjektattribute von ihrer Klasse gespeichert werden, die Klassenattribute von ihrer Klasse, die Metaklassen sind. Dies wird auch bestätigt durch:
quelle
is
ist das Ergebnis , wenn es==
im zweiten Vergleich ersetzt wird, dh sowohl in Python 2.7.15+ als auch in 3.6.8.A.__dict__ is type.__dict__['__dict__'].__get__(A)
False