简单复制:
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__
行为上,例如查找优先级。在For classes, the machinery is in type.__getattribute__()...
下面的"Invoking Descriptors"中的Python片段大致与我认为的type_getattro
中的CPython source大致一致,我通过查看"tp_slots"然后where tp_getattro is populated找到了它。事实上B.v
最初打印{
我不明白的是,为什么赋值B.v = 3
盲目重写描述符,而不是触发{type_setattro
appears to be一个围绕_PyObject_GenericSetAttrWithDict的薄包装。我困惑的症结是:_PyObject_GenericSetAttrWithDict
似乎有{a12}!!考虑到这一点,我不明白为什么B.v = 3
盲目地重写{
免责声明1:我没有用printfs从源代码重建Python,所以我没有
完全确定type_setattro
是在B.v = 3
期间被调用的内容。在
免责声明2:VocalDescriptor
不是为了举例说明“典型”或“推荐”的描述符定义。告诉我何时调用方法是一个冗长的无操作。在
除非有任何重写,}则等价于
B.v
等同于type.__getattribute__(B, "v")
,而{object.__getattribute__(b, "v")
。如果定义了结果,这两个定义都会调用结果的__get__
方法。在请注意,对}传递实例本身。在这两种情况下,
__get__
的调用在每种情况下都是不同的。B.v
传递None
作为第一个参数,而{B
都作为第二个参数传递。在另一方面,
B.v = 3
相当于type.__setattr__(B, "v", 3)
,它不调用__set__
。在B.v = 3
只是用一个整数覆盖描述符(应该如此),这是正确的。在为了使
B.v = 3
调用描述符,描述符应该在元类上定义,即在type(B)
上定义。在要调用
B
上的描述符,可以使用一个实例:B().v = 3
将执行此操作。在
^{pr2}$B.v
调用getter的原因是允许返回描述符实例本身。通常您会这样做,以允许通过类对象访问描述符:现在,}的所有实例之间共享。在
B.v
将返回一些实例,如<mymodule.VocalDescriptor object at 0xdeadbeef>
,您可以与之交互。它实际上是描述符对象,定义为类属性,其状态B.v.__dict__
在{当然,这取决于用户的代码来定义他们想要}只是一种常见模式。在
B.v
做什么,返回{相关问题 更多 >
编程相关推荐