Простое воспроизведение:
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
не предназначен для иллюстрации «типичного» или «рекомендуемого» определения дескриптора. Это многословный запрет на опросы, когда вызываются методы.