Прежде всего 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__
является специальным прокси-объектом, который притворяется словарём (но может и не быть внутренним) и не позволяет вам изменять его или заменять другим. , Этот прокси-сервер позволяет вам, помимо всего прочего, получать доступ к атрибутам класса, которые специфичны для него и не определены в одной из его баз.
По умолчанию 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)
.