Co to jest dict. atrybut dict klasy Pythona?

>>> 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 to zrobię A.something = 10, to pójdzie do A.__dict__. Co? na to <attribute '__dict__' of 'A' objects> Znalezione w A.__dict__.__dict__, a kiedy coś zawiera?

Author: JasonMArcher, 2011-02-02

4 answers

Po pierwsze {[0] } różni się od A.__dict__['__dict__'], a pierwszy nie istnieje. Ten ostatni jest atrybutem __dict__, który miałyby instancje klasy. Jest to obiekt deskryptora, który zwraca wewnętrzny słownik atrybutów dla konkretnej instancji. W skrócie, atrybut __dict__ obiektu nie może być przechowywany w __dict__ obiektu, więc dostęp do niego jest możliwy poprzez deskryptor zdefiniowany w klasie.

Aby to zrozumieć, musisz przeczytać dokumentację deskryptora protokół .

Wersja skrócona:

  1. dla instancji klasy A dostęp do instance.__dict__ jest zapewniany przez A.__dict__['__dict__'], który jest taki sam jak vars(A)['__dict__'].
  2. dla klasy A dostęp do A.__dict__ zapewnia type.__dict__['__dict__'] (teoretycznie), który jest taki sam jak vars(type)['__dict__'].

Wersja długa:

Zarówno klasy, jak i Obiekty zapewniają dostęp do atrybutów zarówno za pośrednictwem operatora atrybutów (zaimplementowanego za pomocą klasy lub metaclass __getattribute__), jak i __dict__ atrybut / protokół używany przez vars(ob).

Dla zwykłych obiektów, obiekt __dict__ tworzy oddzielny obiekt dict, który przechowuje atrybuty, a {[12] } 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, a przed wywołaniem __getattr__). Deskryptor __dict__ klasy implementuje dostęp do tego słownika.

  • x.name jest równoznaczne z próbowaniem tych w kolejności: x.__dict__['name'], type(x).name.__get__(x, type(x)), type(x).name
  • x.__dict__ robi to samo, ale pomija pierwszą z oczywistych powodów

Ponieważ nie jest możliwe, aby __dict__ z instance były przechowywane w __dict__ instancji, jest ona dostępna bezpośrednio przez protokół deskryptora i jest przechowywana w specjalnym polu w instancji.

Podobny scenariusz dotyczy klas, chociaż ich {[2] } jest specjalnym obiektem proxy, który udaje słownik (ale może nie być wewnętrznie) i nie pozwala na zmianę lub zastąpienie go innym. Ten serwer proxy umożliwia między innymi dostęp do atrybutów klasy, które są dla niej specyficzne i nie są zdefiniowane w jednej z jej baz.

Domyślnie, vars(cls) pustej klasy zawiera trzy deskryptory - {[2] } do przechowywania atrybutów instancji, __weakref__, który jest używany wewnętrznie przez weakref, oraz docstring klasy. Pierwsze dwa mogą zniknąć, jeśli zdefiniujesz __slots__. Wtedy nie mielibyście __dict__ i __weakref__ atrybuty, ale zamiast tego miałbyś jeden atrybut klasy dla każdego gniazda. Atrybuty instancji nie będą wówczas przechowywane w słowniku, a dostęp do nich będą zapewniane przez odpowiednie deskryptory w klasie.


I na koniec, niespójność, która A.__dict__ różni się od A.__dict__['__dict__'], wynika z faktu, że atrybut __dict__ jest, przez wyjątek, nigdy Nie sprawdzany w vars(A), więc to, co jest prawdą, nie jest prawdą dla praktycznie każdego innego atrybutu, którego byś użył. Na przykład, A.__weakref__ to to samo co A.__dict__['__weakref__']. Jeśli ta niespójność nie istnieje, użycie A.__dict__ nie zadziałałoby i zawsze musiałbyś używać vars(A) zamiast tego.

 110
Author: Rosh Oxymoron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-02 19:34:57

Ponieważ A.__dict__ jest słownikiem przechowującym atrybuty A, A.__dict__['__dict__'] jest bezpośrednim odniesieniem do tego samego atrybutu A.__dict__.

A.__dict__ zawiera (swego rodzaju) odniesienie do siebie. Część "rodzajowa" jest powodem, dla którego wyrażenie A.__dict__ zwraca dictproxy zamiast normalnego dict.

>>> 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'
 11
Author: vz0,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-02 17:14:36

Zróbmy trochę zwiedzania!

>>> A.__dict__['__dict__']
<attribute '__dict__' of 'A' objects>
Ciekawe co to jest?
>>> type(A.__dict__['__dict__'])
<type 'getset_descriptor'>

Jakie atrybuty posiada obiekt getset_descriptor?

>>> type(A.__dict__["__dict__"]).__dict__
<dictproxy object at 0xb7efc4ac>

Wykonując kopię tego dictproxy możemy znaleźć kilka interesujących atrybutów, w szczególności __objclass__ i __name__.

>>> A.__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__
(<class '__main__.A'>, '__dict__')

Więc __objclass__ jest odniesieniem do A i __name__ jest tylko łańcuchem '__dict__', nazwa atrybutu może?

>>> getattr(A.__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__) == A.__dict__
True

Mamy to! A.__dict__['__dict__'] jest obiektem, który może odwoływać się do A.__dict__.

 10
Author: Andrew Clark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-02 17:38:05

Możesz spróbować poniższego prostego przykładu, aby zrozumieć więcej z tego:

>>> 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 klasy są przechowywane przez ich klasę, atrybuty klasy są przechowywane przez ich klasę, które są metaklasami. Jest to również potwierdzone przez:

>>> a.__dict__ == A.__getattribute__(a, '__dict__')
True
>>> A.__dict__ == type.__getattribute__(A, '__dict__')
True
 9
Author: damaZhang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-06 23:52:27