数据描述符和非数据描述符的正确定义分别是什么?

5

这两个文档都是关于Python的:

第一个文档中说:

如果一个对象同时定义了 __get__()__set__(),则被认为是数据描述符。 只定义了 __get__() 的描述符称为非数据描述符(通常用于方法,但也可能有其他用途)。

第二个文档中说:

如果描述符定义了 __set__() 和/或 __delete__(),则它是数据描述符;如果它没有定义,则为非数据描述符。 通常,数据描述符同时定义了 __get__()__set__(),而非数据描述符只有 __get__() 方法。

问题是:仅定义 __set__ 是否足以使描述符成为数据描述符?

当我查阅Python源代码时,发现以下内容:

#define PyDescr_IsData(d) (Py_TYPE(d)->tp_descr_set != NULL)

看起来我们只能定义没有 __get____set__

接下来我会写一些示例来证明我的观点:

class GetSet(object):
def __get__(self, instance, cls =None):
    print('__get__')

def __set__(self, obj, val):
    print('__set__')

class Get(object):
    def __get__(self, instance, cls =None):
        print('__get__')

class Set(object):
    def __set__(self, obj, val):
        print('__set__')

class UserClass(object):
    a = Get()
    b = Set()
    c = GetSet()


u = UserClass()
u.__dict__['a'] = 'a'
u.__dict__['b'] = 'b'
u.__dict__['c'] = 'c'

print('start')
print(u.a)
print(u.b)
print(u.c)

输出结果又让我感到困惑了:
start
a
b
__get__
None

根据Python属性查找顺序:数据描述符的优先级高于obj.__dict__
我的例子表明:只有同时定义了__set____get__才能将其视为数据描述符!
哪个答案是正确的? __set__ --> 数据描述符
还是 __get____set__ --> 数据描述符?

你的两个链接都指向同一个页面。 - user2357112
1个回答

7
第二个引用是正确的。第二个引用来自于 Python语言参考(尽管您提供了错误的链接),而语言参考被认为比how-to指南更具权威性。此外,它与实际行为相匹配; 您找到的PyDescr_IsData宏是在中使用的实际例程来确定什么算作数据描述符,而任一个__set____delete__ 都将使tp_descr_set 非空。
语言参考还解释了为什么Set 不会覆盖a.b 的实例字典:
如果一个描述符没有定义__get__(),那么访问该属性将返回描述符对象本身,除非对象的实例字典中有一个值。[...] 定义了__set__()__get__()的数据描述符总是会覆盖实例字典中的重新定义。
定义__set____delete__将设置类型的tp_descr_set插槽,并使类型的实例成为数据描述符。数据描述符将始终被调用来尝试设置或删除它管理的属性,即使实例字典中有相同名称的条目,即使它只有__set__并且您正在尝试删除属性或反之亦然。(如果它没有所需的方法,它将引发异常。)如果数据描述符还具有__get__,它也将拦截尝试获取属性;否则,Python将回退到正常的属性查找行为,就好像它根本不是描述符。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接