数据描述符和非数据描述符的正确定义是哪一个?
问题内容:
它们都是文档中的python:
第一个说:
如果对象同时定义
__get__()
和__set__()
,则将其视为数据描述符。仅定义__get__()
的描述符称为非数据描述符(它们通常用于方法,但也可以用于其他用途)。
第二个说:
如果描述符定义
__set__()
和/或__delete__()
,则为数据描述符;如果没有定义,则为非数据描述符。通常,数据描述符同时定义__get__()
和__set__()
,而非数据描述符仅定义__get__()
方法。
问题是:仅定义__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语言参考(尽管您提供了错误的链接),并且该语言参考被认为比使用指南更具权威性。而且,它与实际行为匹配;在PyDescr_IsData
你发现宏是实际的程序中使用object.__getattribute__
,以确定作为数据描述什么计数,并且无论是__set__
或__delete__
将导致tp_descr_set
对非空。
语言参考还解释了为什么Set
不覆盖以下实例的dict a.b
:
如果未定义
__get__()
,则访问属性将返回描述符对象本身,除非对象的实例字典中没有值。[…]带有__set__()
和__get__()
定义的数据描述符始终会覆盖实例字典中的重定义。
定义__set__
或__delete__
将设置类型的tp_descr_set
插槽并创建类型数据描述符的实例。数据描述符将始终被调用以尝试设置或删除其管理的属性,即使实例dict中有一个条目具有相同的名称,即使该条目仅具有__set__
且您正试图删除该属性也是如此。反之亦然。(如果没有所需的方法,它将引发异常。)如果数据描述符也具有__get__
,则它还将拦截获取属性的尝试;否则,Python将退回到常规的属性查找行为,就好像它根本不是描述符。