简单再现:
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:我没有使用printfs从源代码重建Python,所以我不确定type_setattro
到底是在调用什么B.v = 3
。
免责声明2: VocalDescriptor
无意举例说明“典型”或“推荐”描述符定义。告诉我何时调用方法是一个冗长的操作。
__get__
奏效,而不是为什么__set__
没有奏效。
__get__
方法。 B.v = 3
有效地使用覆盖了该属性int
。
__get__
被调用,并且的默认的实现object.__getattribute__
和type.__getattribute__
调用__get__
使用实例或类时。通过分配__set__
仅是实例。