Перш за все A.__dict__.__dict__
відрізняється від A.__dict__['__dict__']
, а першого не існує. Останнє є __dict__
атрибутом, який мали б екземпляри класу. Це об’єкт дескриптора, який повертає внутрішній словник атрибутів для конкретного екземпляра. Коротше кажучи, __dict__
атрибут об'єкта не може зберігатися в об'єкті __dict__
, тому до нього можна отримати доступ через дескриптор, визначений у класі.
Щоб це зрозуміти, вам доведеться прочитати документацію протоколу дескрипторів .
Коротка версія:
- Для екземпляра класу
A
доступ до instance.__dict__
якого надається A.__dict__['__dict__']
таким самим, що і vars(A)['__dict__']
.
- Для класу A доступ до
A.__dict__
забезпечує type.__dict__['__dict__']
(теоретично), що є таким самим, як vars(type)['__dict__']
.
Довга версія:
І класи, і об'єкти забезпечують доступ до атрибутів як через оператор атрибутів (реалізований через клас або метаклас __getattribute__
), так і через __dict__
атрибут / протокол, який використовується vars(ob)
.
Для звичайних об'єктів __dict__
об'єкт створює окремий dict
об'єкт, який зберігає атрибути, і __getattribute__
спочатку намагається отримати до нього доступ і отримати атрибути звідти (перед спробою шукати атрибут у класі за допомогою протоколу дескриптора та перед викликом __getattr__
). __dict__
Дескриптор на клас реалізує доступ до цього словника.
x.name
рівносильна спробі тих , в наступному порядку: x.__dict__['name']
, type(x).name.__get__(x, type(x))
,type(x).name
x.__dict__
робить те саме, але пропускає перший із зрозумілих причин
Як це неможливо для __dict__
з instance
зберігати в __dict__
екземплярі, він доступний через протокол дескриптора безпосередньо замість цього, і зберігається в спеціальному полі в екземплярі.
Подібний сценарій справедливий для класів, хоча вони __dict__
є спеціальним проксі-об'єктом, який видає себе за словник (але може бути не внутрішнім) і не дозволяє змінити його або замінити іншим. Цей проксі дозволяє, крім усього іншого, отримувати доступ до атрибутів класу, які є специфічними для нього і не визначені в одній з його баз.
За замовчуванням a vars(cls)
порожнього класу містить три дескриптори - __dict__
для зберігання атрибутів екземплярів, __weakref__
що використовуються всередині weakref
, та документації класу. Перші два можуть зникнути, якщо ви визначите __slots__
. Тоді у вас не було б атрибутів __dict__
і __weakref__
атрибутів, але натомість у вас був би один атрибут класу для кожного слота. Тоді атрибути екземпляра не будуть зберігатися у словнику, і доступ до них забезпечуватимуть відповідні дескриптори класу.
І нарешті, невідповідність, яка A.__dict__
відрізняється від, A.__dict__['__dict__']
полягає в тому, що атрибут __dict__
, за винятком, ніколи не шукався vars(A)
, тому те, що для нього відповідає дійсності, не відповідає практично будь-якому іншому атрибуту, який ви використовуєте. Наприклад, A.__weakref__
це те саме, що A.__dict__['__weakref__']
. Якби цієї невідповідності не існувало, використання A.__dict__
не працювало б, і vars(A)
замість цього вам довелося б завжди використовувати .
ive
. Принаймні, це зробило б ще більшеA.__dict__['ive']
запитання;) Я себе