2016-11-07 4 views
0

Я читал о дескрипторы в Descriptor HowTo Guide, и я смущен этим предложением:атрибуты Python и дескрипторы

Если словарь Экземпляр имеет запись с таким же именем, как дескриптор данных, дескриптор данных имеет приоритет.

Как словарь может содержать два элемента (обычный ввод и дескриптор данных) с тем же именем? Или являются атрибутами, которые являются дескрипторами, не сохраненными в __dict__?

ответ

3

Дескриптор Данные живет в пространстве имен класса, в то время как экземпляр атрибута жизни в экземпляра имен (так instance.__dict__). Это два отдельных словаря, поэтому здесь нет конфликта.

Таким образом, для любого заданного поиска атрибута для имени foo на экземпляре bar, Python также смотрит на своего класса (type(bar), названный C ниже), в следующем порядке:

  1. C.foo ищется. Если это дескриптор данных , здесь заканчивается поиск. C.foo.__get__(bar, C) возвращен. В противном случае Python сохранит этот результат для шага 3 (не раздумывая дважды).

  2. Если C.foo не существует или является обычным атрибутом, тогда Python ищет bar.__dict__['foo']. Если он существует, он возвращается. Обратите внимание, что эта часть никогда не достигается, если C.foo является дескриптором данных!

  3. Если 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' 
+0

ОК, поэтому дескрипторы данных находятся в словаре классов. «Descriptor HowTo Guide», по-видимому, предполагает, что поиск словаря экземпляра выполняется до словаря класса: «Например, ax имеет цепочку поиска, начинающуюся с .__ dict __ ['x'], затем введите (a) .__ dict __ [ 'x'] и продолжается через базовые классы типа (a), исключая метаклассы. » –

+0

@SirVisto упрощается там. Если вы взяли дескрипторы данных из картинки, порядок будет совершенно правильным.Представление дескрипторов данных в этот момент бесполезно осложняло бы рассказ. –

+0

На самом деле статья разъясняет это позже: «Реализация работает через цепочку приоритетов, которая дает приоритеты дескрипторам данных над переменными экземпляра, приоритет переменных экземпляра над дескрипторами без данных и присваивает самый низкий приоритет __getattr __(), если он предоставлен». –

1

Рассмотрим следующий фрагмент кода:

class X: 
    @property 
    def x(self): 
     return 2 

x1 = X() 
x1.__dict__['x'] = 1 
print(x1.x) 

Этот код печатает 2, потому что дескриптор данных (определенный в классе) имеет приоритет над словарем экземпляра.

+0

В чем причина этого? Я бы интуитивно ожидал, что атрибут экземпляра будет теневым атрибутом класса с тем же именем ... –

+0

@SirVisto: это позволяет дескриптору данных полностью контролировать доступ (включая включение и удаление). Если экземпляр dict получил приоритет, тогда 'del inst.foo' иногда * доходил бы до экземпляра, * иногда * дескриптора данных. Для настройки, что должно произойти, когда вы выполняете 'inst.foo = 'value'', должно ли это иметь значение, если уже есть атрибут экземпляра? Кто тогда отвечает за обработку настроек? Постоянно * всегда * делая ответственный дескриптор данных ответственным за то, чтобы вы разрешали все эти вопросы четко и последовательно. –

+0

@SirVisto: еще одна мотивация - это возможность убедиться, что некоторые атрибуты класса не могут быть случайно переопределены в экземпляре. Например, атрибут '__class__' никогда не должен быть установлен в экземпляре, потому что тогда вы больше не можете найти соответствующий класс, поэтому' __class__' является дескриптором данных в классе. –