>>> 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?
Jeśli tak A.something = 10
, to przechodzi do A.__dict__
. Co jest tego <attribute '__dict__' of 'A' objects>
znaleźć w A.__dict__.__dict__
, i kiedy to ma zawierać coś?
python
class
metaprogramming
magic-methods
porgarmingduod
źródło
źródło
ive
. Przynajmniej sprawiłoby to więcejA.__dict__['ive']
pytań;) zobaczę się pozaOdpowiedzi:
Przede wszystkim
A.__dict__.__dict__
jest innyA.__dict__['__dict__']
, a tego pierwszego nie ma. Ten ostatni jest__dict__
atrybutem, który miałyby wystąpienia klasy. Jest to obiekt deskryptora, który zwraca wewnętrzny słownik atrybutów dla określonej instancji. Krótko mówiąc,__dict__
atrybut obiektu nie może być przechowywany w obiekcie__dict__
, więc jest dostępny za pośrednictwem deskryptora zdefiniowanego w klasie.Aby to zrozumieć, musiałbyś przeczytać dokumentację protokołu deskryptora .
Krótka wersja:
A
, dostępinstance.__dict__
jest zapewniany przezA.__dict__['__dict__']
to samo, covars(A)['__dict__']
.A.__dict__
zapewniatype.__dict__['__dict__']
(w teorii) to samo, covars(type)['__dict__']
.Wersja długa:
Zarówno klasy, jak i obiekty zapewniają dostęp do atrybutów zarówno za pośrednictwem operatora atrybutu (zaimplementowanego za pośrednictwem klasy lub metaklasy
__getattribute__
), jak i__dict__
atrybutu / protokołu, który jest używany przezvars(ob)
.W przypadku normalnych obiektów
__dict__
obiekt tworzy oddzielnydict
obiekt, który przechowuje atrybuty i__getattribute__
najpierw próbuje uzyskać do niego dostęp i stamtąd pobrać atrybuty (przed próbą wyszukania atrybutu w klasie przy użyciu protokołu deskryptora i przed wywołaniem__getattr__
).__dict__
Deskryptor od klasy implementuje dostęp do tego słownika.x.name
jest równoważne próbuje te w kolejności:x.__dict__['name']
,type(x).name.__get__(x, type(x))
,type(x).name
x.__dict__
robi to samo, ale pomija pierwszy z oczywistych powodówJak to jest możliwe, aby
__dict__
zinstance
być przechowywane w__dict__
instancji, jest on dostępny za pośrednictwem protokołu deskryptora zamiast bezpośrednio, i jest przechowywany w specjalnym polu w instancji.Podobny scenariusz jest prawdziwy dla klas, chociaż ich
__dict__
jest specjalnym obiektem proxy, który udaje słownik (ale może nie być wewnętrznie) i nie pozwala na jego zmianę lub zastąpienie innym. To proxy umożliwia między innymi dostęp do atrybutów klasy, które są dla niej specyficzne i nie są zdefiniowane w żadnej z jej baz.Domyślnie
vars(cls)
klasa pustej zawiera trzy deskryptory -__dict__
do przechowywania atrybutów instancji,__weakref__
które są używane wewnętrznie przezweakref
klasę, oraz do jej opisu. Pierwsze dwa mogą zniknąć, jeśli zdefiniujesz__slots__
. Wtedy nie miałbyś atrybutów__dict__
i__weakref__
, ale zamiast tego miałbyś jeden atrybut klasy dla każdego gniazda. Atrybuty instancji nie byłyby wtedy przechowywane w słowniku, a dostęp do nich zapewnią odpowiednie deskryptory w klasie.I wreszcie, niespójność, która
A.__dict__
różni się odA.__dict__['__dict__']
tego, że atrybut__dict__
jest wyjątkowy, nigdy nie jest sprawdzanyvars(A)
, więc to, co jest prawdą, nie jest prawdą dla praktycznie każdego innego atrybutu, którego użyjesz. Na przykładA.__weakref__
to to samo, coA.__dict__['__weakref__']
. Gdyby ta niespójność nie istniała, używanieA.__dict__
nie działałoby i musiałbyś zawsze używaćvars(A)
zamiast tego.źródło
__dict__
można przechowywać atrybutu obiektu w atrybucie obiektu__dict__
?__dict__
ma na celu przechowywanie wszystkich atrybutów instancji, dostęp do atrybutu formularzaobj.x
jest ostatecznie sprawdzany w obiekcie__dict__
, a mianowicieobj.__dict__['x']
. Teraz, gdyby__dict__
nie został zaimplementowany jako deskryptor, doprowadziłoby to do nieskończonej rekurencji, ponieważ aby uzyskać dostępobj.__dict__
, musiałbyś sprawdzić to jakoobj.__dict__['__dict__']
. Deskryptor omija ten problem.Ponieważ
A.__dict__
jest słownikiem przechowującymA
atrybuty,A.__dict__['__dict__']
jest bezpośrednim odniesieniem do tego samegoA.__dict__
atrybutu.A.__dict__
zawiera (rodzaj) odniesienie do siebie. Część typu „rodzaj” jest powodem, dla którego wyrażenieA.__dict__
zwraca adictproxy
zamiast normalnejdict
.>>> 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'
źródło
A.__dict__['__dict__']
nie jest odniesieniem doA.__dict__
. Implementuje__dict__
atrybut instancji. Aby spróbować tego samodzielnie,A.__dict__['__dict__'].__get__(A(), A)
zwraca atrybutyA()
, whileA.__dict__['__dict__'].__get__(A, type)
kończy się niepowodzeniem.Zróbmy trochę odkrywania!
>>> A.__dict__['__dict__'] <attribute '__dict__' of 'A' objects>
Zastanawiam się, co to jest?
>>> type(A.__dict__['__dict__']) <type 'getset_descriptor'>
Jakie atrybuty ma
getset_descriptor
obiekt?>>> type(A.__dict__["__dict__"]).__dict__ <dictproxy object at 0xb7efc4ac>
Tworząc kopię tego
dictproxy
możemy znaleźć kilka interesujących atrybutów,__objclass__
a konkretnie i__name__
.>>> A.__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__ (<class '__main__.A'>, '__dict__')
Czy
__objclass__
jest więc odniesieniem do łańcucha ,A
a__name__
może tylko'__dict__'
nazwą atrybutu?>>> getattr(A.__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__) == A.__dict__ True
Mamy to!
A.__dict__['__dict__']
to obiekt, do którego można się odwołaćA.__dict__
.źródło
__objclass__
to klasa definiuje ten atrybut, a nie jest to atrybut tej klasy. To sprawia, że twójgetattr
przykład jest nieprawidłowy. Bardziej poprawne byłobygetattr(A().__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__)
KeyError: '__dict__'
, w przeciwieństwie do @ AndrewClark.Możesz spróbować następującego prostego przykładu, aby lepiej to zrozumieć:
>>> 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
Z powyższego przykładu wynika, że atrybuty obiektów klas są przechowywane przez ich klasę, atrybuty klas są przechowywane przez ich klasę, którą są metaklasy. Jest to również potwierdzone przez:
>>> a.__dict__ == A.__getattribute__(a, '__dict__') True >>> A.__dict__ == type.__getattribute__(A, '__dict__') True
źródło
is
zostanie zastąpione==
, tjA.__dict__ is type.__dict__['__dict__'].__get__(A)
. Wynik jestFalse
zarówno w Pythonie 2.7.15+, jak i 3.6.8.