Простое воспроизведение:
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 фрагмент кода в «Вызов Дескрипторы» чуть ниже 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__.