【发布时间】:2019-10-16 19:09:08
【问题描述】:
简单的复制:
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
这个问题有一个effective duplicate,但没有回答重复的问题,我在 CPython 源代码中挖掘了更多作为学习练习。警告:我进入了杂草。我真的希望我能从a captain who knows those waters 那里得到帮助。为了我自己和未来读者的利益,我试图尽可能明确地追踪我正在查看的电话。
我已经看到很多墨水溢出了 __getattribute__ 应用于描述符的行为,例如查找优先级。 "Invoking Descriptors" 中的 Python sn-p 就在 For classes, the machinery is in type.__getattribute__()... 下方,在我看来与我认为 type_getattro 中对应的 CPython source 大致一致,我通过查看 "tp_slots" 然后查看 where tp_getattro is populated 来追踪它。 B.v 最初打印 __get__, obj=None, objtype=<class '__main__.B'> 的事实对我来说很有意义。
我不明白的是,为什么赋值B.v = 3会盲目地覆盖描述符,而不是触发v.__set__?我试图追踪 CPython 调用,再次从"tp_slots" 开始,然后查看where tp_setattro is populated,然后查看type_setattro。 type_setattroappears to be_PyObject_GenericSetAttrWithDict 周围的薄包装。这就是我困惑的症结所在:_PyObject_GenericSetAttrWithDict 似乎有logic that gives precedence to a descriptor's __set__ method!考虑到这一点,我不明白为什么B.v = 3 会盲目地覆盖v 而不是触发v.__set__。
免责声明 1:我没有使用 printfs 从源代码重建 Python,所以我不是
完全确定 type_setattro 是在 B.v = 3 期间调用的内容。
免责声明 2:VocalDescriptor 并非旨在举例说明“典型”或“推荐”描述符定义。告诉我何时调用方法是一个冗长的无操作。
【问题讨论】:
-
对我来说,这在最后一行打印 3... 代码工作正常
-
从实例访问属性时应用描述符,而不是类本身。对我来说,奥秘在于为什么
__get__会起作用,而不是为什么__set__不起作用。 -
@Jab OP 预计仍会调用
__get__方法。B.v = 3已经用int有效地覆盖了属性。 -
@jasonharper 属性访问决定是否调用
__get__,object.__getattribute__和type.__getattribute__的默认实现在使用实例或类时调用__get__。通过__set__分配 仅限实例。 -
@jasonharper 我相信描述符的
__get__方法应该在从类本身调用时触发。根据how-to guide,这就是@classmethods 和@staticmethods 的实现方式。 @Jab 我想知道为什么B.v = 3能够覆盖类描述符。基于 CPython 实现,我预计B.v = 3也会触发__set__。
标签: python cpython python-descriptors