Просте запитання:
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__
.