Дескриптор Данные живет в пространстве имен класса, в то время как экземпляр атрибута жизни в экземпляра имен (так instance.__dict__
). Это два отдельных словаря, поэтому здесь нет конфликта.
Таким образом, для любого заданного поиска атрибута для имени foo
на экземпляре bar
, Python также смотрит на своего класса (type(bar)
, названный C
ниже), в следующем порядке:
C.foo
ищется. Если это дескриптор данных , здесь заканчивается поиск. C.foo.__get__(bar, C)
возвращен. В противном случае Python сохранит этот результат для шага 3 (не раздумывая дважды).
Если C.foo
не существует или является обычным атрибутом, тогда Python ищет bar.__dict__['foo']
. Если он существует, он возвращается. Обратите внимание, что эта часть никогда не достигается, если C.foo
является дескриптором данных!
Если bar.__dict__['foo']
не существует, но C.foo
существует, то C.foo
используется. Если C.foo
- дескриптор (не данных), возвращается C.foo.__get__(bar, C)
.
(Обратите внимание, что C.foo
действительно C.__dict__['foo']
, но для простоты я проигнорировал доступ дескриптора по классам в приведенном выше).
Возможно, конкретный пример помогает; вот два дескрипторы, один является дескриптором данных (есть метод __set__
), а другой не описатель данных:
>>> class DataDesc(object):
... def __get__(self, inst, type_):
... print('Accessed the data descriptor')
... return 'datadesc value'
... def __set__(self, inst, value):
... pass # just here to make this a data descriptor
...
>>> class OtherDesc(object):
... def __get__(self, inst, type_):
... print('Accessed the other, non-data descriptor')
... return 'otherdesc value'
...
>>> class C(object):
... def __init__(self):
... # set two instance attributes, direct access to not
... # trigger descriptors
... self.__dict__.update({
... 'datadesc': 'instance value for datadesc',
... 'otherdesc': 'instance value for otherdesc',
... })
... datadesc = DataDesc()
... otherdesc = OtherDesc()
...
>>> bar = C()
>>> bar.otherdesc # non-data descriptor, the instance wins
'instance value for otherdesc'
>>> bar.datadesc # data descriptor, the descriptor wins
Accessed the data descriptor
'datadesc value'
ОК, поэтому дескрипторы данных находятся в словаре классов. «Descriptor HowTo Guide», по-видимому, предполагает, что поиск словаря экземпляра выполняется до словаря класса: «Например, ax имеет цепочку поиска, начинающуюся с .__ dict __ ['x'], затем введите (a) .__ dict __ [ 'x'] и продолжается через базовые классы типа (a), исключая метаклассы. » –
@SirVisto упрощается там. Если вы взяли дескрипторы данных из картинки, порядок будет совершенно правильным.Представление дескрипторов данных в этот момент бесполезно осложняло бы рассказ. –
На самом деле статья разъясняет это позже: «Реализация работает через цепочку приоритетов, которая дает приоритеты дескрипторам данных над переменными экземпляра, приоритет переменных экземпляра над дескрипторами без данных и присваивает самый низкий приоритет __getattr __(), если он предоставлен». –