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