简单的复制:
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
并非旨在举例说明“典型”或“推荐”描述符定义。告诉我何时调用方法是一个冗长的无操作。