Путаница по поводу дескрипторов Python и ‹Descriptor HowTo Guide›

Недавно я прочитал официальный HOW-TO по дескрипторам Python, который фактически взят из эссе, написанное Рэймондом Хеттингером давным-давно. Но прочитав его несколько раз, я все еще не понимаю некоторые его части. Я процитирую несколько абзацев, а затем мои недоумения и вопросы.


Если в словаре экземпляра есть запись с тем же именем, что и у дескриптора данных, дескриптор данных имеет приоритет. Если в словаре экземпляра есть запись с тем же именем, что и у дескриптора, не относящегося к данным, то запись словаря имеет приоритет.

  • То же самое и с классом? Какова цепочка приоритетов класса, если в его словаре есть запись с тем же именем, что и у дескриптора данных/не данных?

Для объектов механизм находится в object.__getattribute__(), который преобразует b.x в type(b).__dict__['x'].__get__(b, type(b)). Реализация работает через цепочку приоритетов, которая дает дескрипторам данных приоритет над переменными экземпляра, приоритет переменных экземпляра над дескрипторами, не относящимися к данным, и присваивает самый низкий приоритет __getattr__(), если он предоставлен.

Для классов механизм находится в type.__getattribute__(), который преобразует B.x в B.__dict__['x'].__get__(None, B).

  • The above two paragraphs tell the process of a descriptor's being invoked automatically upon attribute access. It lists the difference between attribute's being accessed by an instance (b.x) and a class (B.x). However, here are my confusions:
    • if the attribute of a class or an instance is not a descriptor, will the transformation (i.e., transforms b.x into type(b).__dict__['x'].__get__(b, type(b)) and B.x into B.__dict__['x'].__get__(None, B)) still proceed? Is returning the attribute in this class's or instance's dict directly simpler?
    • Если в словаре экземпляра есть запись с тем же именем, что и у дескриптора, не относящегося к данным, в соответствии с правилом приоритета в первой цитате, запись словаря имеет приоритет, будет ли в это время продолжаться преобразование? Или просто вернуть значение в его dict?

Дескрипторы, не относящиеся к данным, обеспечивают простой механизм для вариаций обычных шаблонов связывания функций в методы.

  • Выбраны ли дескрипторы, не относящиеся к данным, потому что функции/методы можно только получить, но нельзя установить?
  • Каков основной механизм привязки функций к методам? Поскольку словари классов хранят методы как функции, если мы вызываем один и тот же метод, используя класс и его экземпляр соответственно, как базовая функция может определить, должен ли ее первый аргумент быть self или нет?

Функции имеют метод __get__(), поэтому их можно преобразовать в метод при доступе к ним как к атрибутам. Дескриптор без данных преобразует вызов obj.f(*args) в f(obj, *args). Вызов klass.f(*args) становится f(*args).

  • Как дескриптор без данных может преобразовать вызов obj.f(*args) в f(obj, *args)?
  • Как дескриптор без данных может преобразовать вызов klass.f(*args) в f(*args)?
  • Каков основной механизм двух вышеупомянутых преобразований? Почему существуют различия между классом и экземпляром?
  • Какую роль играет метод __get__() в вышеуказанных обстоятельствах?

person DeepMind    schedule 19.04.2013    source источник


Ответы (1)


То же самое и с классом? Какова цепочка приоритетов класса, если в его словаре есть запись с тем же именем, что и у дескриптора данных/не данных?

Нет, если атрибут определен как в суперклассе, так и в подклассе, значение суперкласса полностью игнорируется.

если атрибут класса или экземпляра не является дескриптором, будет ли продолжаться преобразование (т. е. преобразовывать b.x в type(b).__dict__['x'].__get__(b, type(b)) и B.x в B.__dict__['x'].__get__(None, B))?

Нет, он возвращает непосредственно объект, полученный из класса __dict__. Или, что то же самое, да, если вы сделаете вид, что все объекты по умолчанию имеют метод __get__(), который игнорирует свои аргументы и возвращает self.

Если в словаре экземпляра есть запись с тем же именем, что и у дескриптора, не относящегося к данным, в соответствии с правилом приоритета в первой цитате, запись словаря имеет приоритет, будет ли преобразование продолжаться в это время? Или просто вернуть значение в свой dict?

Что неясно в процитированном вами абзаце (возможно, это записано в другом месте), так это то, что когда b.x решает вернуть b.__dict__['x'], __get__ в любом случае не вызывается. __get__ вызывается именно тогда, когда синтаксис b.x или B.x решает вернуть объект, который находится в словаре class.

Выбраны ли неданные дескрипторы, потому что функции/методы можно только получить, но нельзя установить?

Да: они являются обобщением модели классов «старого стиля» в Python, в которой вы можете сказать B.f = 42, даже если f является функциональным объектом, живущим в классе B. Это позволяет переопределить функциональный объект несвязанным объектом. С другой стороны, дескрипторы данных имеют другую логику для поддержки property.

Каков основной механизм привязки функций к методам? Поскольку словари классов хранят методы как функции, если мы вызываем один и тот же метод, используя класс и его экземпляр соответственно, как базовая функция может определить, должен ли ее первый аргумент быть self или нет?

Чтобы понять это, вам нужно иметь в виду «объекты методов». Синтаксис b.f(*args) эквивалентен (b.f)(*args), то есть двум шагам. Первый шаг вызывает f.__get__(b); это возвращает объект метода, который хранит как b, так и f. Второй шаг вызывает объект метода, который, в свою очередь, вызовет исходный f, добавив b в качестве дополнительного аргумента. Этого не происходит для B.f просто потому, что B.f, то есть f.__get__(None, B), это просто f (в ​​Python 3). Именно так разработан специальный метод __get__ для объектов-функций.

person Armin Rigo    schedule 19.04.2013