Просте запитання:
class VocalDescriptor(object):
def __get__(self, obj, objtype):
print('__get__, obj={}, objtype={}'.format(obj, objtype))
def __set__(self, obj, val):
print('__set__')
class B(object):
v = VocalDescriptor()
B.v # prints "__get__, obj=None, objtype=<class '__main__.B'>"
B.v = 3 # does not print "__set__", evidently does not trigger descriptor
B.v # does not print anything, we overwrote the descriptor
На це питання є ефективний дублікат , але на дублікат не було відповіді, і я трохи більше заглибився у джерело CPython як навчальну вправу. Попередження: я зайшов у бур’яни. Я дуже сподіваюся, що зможу отримати допомогу від капітана, який знає ці води . Я намагався бути максимально чітким у відстеженні дзвінків, на які я дивився, задля власної майбутньої вигоди та користі майбутніх читачів.
Я бачив багато чорнила, що пролилося на поведінку __getattribute__застосованих до дескрипторів, наприклад, пріоритет пошуку. Фрагмент Python у "Invoking Descriptors" трохи нижче For classes, the machinery is in type.__getattribute__()...приблизно відповідає моїй думці з тим, що, на мою думку, є відповідним джерелом CPython, в type_getattroякому я відстежив, дивлячись на "tp_slots", а потім там, де tp_getattro заселений . А те, що B.vспочатку друкує, __get__, obj=None, objtype=<class '__main__.B'>має сенс для мене.
Що я не розумію, це те, чому завдання B.v = 3сліпо переписує дескриптор, а не спрацьовує v.__set__? Я спробував простежити виклик CPython, починаючи ще раз з "tp_slots" , потім дивлячись, де tp_setattro заселений , потім дивлячись на type_setattro . type_setattro виявляється тонкою обгорткою навколо _PyObject_GenericSetAttrWithDict . І в цьому суть моєї плутанини: _PyObject_GenericSetAttrWithDictсхоже, є логіка, яка надає перевагу дескрипторному __set__методу !! Зважаючи на це, я не можу зрозуміти, чому B.v = 3сліпо переписує, vа не викликає v.__set__.
Застереження 1: Я не відновлював Python з джерела з printfs, тому я не зовсім впевнений type_setattro, що називається під час B.v = 3.
Відмова від відповідальності 2: VocalDescriptorне призначений для прикладу визначення "типового" або "рекомендованого" дескриптора. Це багатослівний не-оп, щоб сказати мені, коли методи викликаються.
__get__, чому __set__взагалі працювали, а не чому ні.
__get__метод. B.v = 3ефективно перезаписав атрибут на int.
__get__викликається, а також реалізація за замовчуванням object.__getattribute__та type.__getattribute__виклик __get__при використанні екземпляра чи класу. Призначення через __set__лише екземпляр.
__get__методи дескрипторів повинні спрацьовувати, коли викликаються з самого класу. Ось як реалізуються @classmethods та @staticmethods відповідно до інструкції . @Jab Мені цікаво, чому B.v = 3можна перезаписати дескриптор класу. На основі реалізації CPython, я очікував, що B.v = 3він також спрацьовує __set__.