哪个是数据描述符和非数据描述符的正确定义?

which one is the right definition of data-descriptor and non-data descriptor?

他们都是python来自文档:

the first one 说:

If an object defines both __get__() and __set__(), it is considered a data descriptor. Descriptors that only define__get__() are called non-data descriptors (they are typically used for methods but other uses are possible).

the second one 说:

If the descriptor defines __set__() and/or __delete__(), it is a data descriptor; if it defines neither, it is a non-data descriptor. Normally, data descriptors define both __get__() and __set__(), while non-data descriptors have just the __get__() method.

问题是:仅定义 __set__ 是否足以创建数据描述符?

我们参考了 python 源代码,我发现了这个:

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

似乎我们只能定义 __set__ 而没有 __get__.

然后我转写一些例子来证明我得到了什么:

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__ ---> 数据描述符 ?

第二个引用是正确的。第二个引号来自 Python language reference (though you've provided the wrong link), and the language reference is considered more authoritative than how-to guides. Also, it matches the actual behavior; the PyDescr_IsData macro you found is the actual routine used in object.__getattribute__ to determine what counts as a data descriptor, and either __set__ or __delete__ 将导致 tp_descr_set 为非空。

语言参考还解释了为什么 Set 不覆盖 a.b:

的实例字典

If it does not define __get__(), then accessing the attribute will return the descriptor object itself unless there is a value in the object’s instance dictionary. [...] Data descriptors with __set__() and __get__() defined always override a redefinition in an instance dictionary.

定义 __set____delete__ 将设置类型的 tp_descr_set 槽并生成类型数据描述符的实例。数据描述符将始终被调用以尝试设置或删除它管理的属性,即使实例的字典中有一个具有相同名称的条目,即使它只有 __set__ 而你正在尝试删除属性,反之亦然。 (如果它没有所需的方法,它会引发异常。)如果数据描述符也有 __get__,它也会拦截获取属性的尝试;否则,Python 将退回到正常的属性查找行为,就好像它根本不是描述符一样。