我一直在阅读Descriptor HowTo Guide中的描述符,我对这句话感到困惑:
如果实例的字典中有一个与数据描述符同名的条目,则数据描述符优先。
字典如何包含两个具有相同名称的项目(一个普通条目和一个数据描述符)?或者是描述符的属性没有存储在__dict__
?
我一直在阅读Descriptor HowTo Guide中的描述符,我对这句话感到困惑:
如果实例的字典中有一个与数据描述符同名的条目,则数据描述符优先。
字典如何包含两个具有相同名称的项目(一个普通条目和一个数据描述符)?或者是描述符的属性没有存储在__dict__
?
数据描述符位于类名称空间中,而实例属性位于实例名称空间中(so instance.__dict__
)。这是两个独立的字典,所以这里没有冲突。
foo
因此,对于实例名称的任何给定属性查找bar
,Python 也会按以下顺序查看它的类(如下type(bar)
命名C
):
C.foo
被查找。如果它是一个数据描述符,这就是查找结束的地方。C.foo.__get__(bar, C)
被退回。否则,Python 将存储此结果以供第 3 步使用(无需查找两次)。
如果C.foo
不存在或者是常规属性,则 Python 会查找bar.__dict__['foo']
. 如果存在,则返回。请注意,如果C.foo
是数据描述符,则永远不会到达此部分!
如果bar.__dict__['foo']
不存在,但C.foo
存在,则C.foo
使用。如果C.foo
是(非数据)描述符,C.foo.__get__(bar, C)
则返回。
(请注意,这C.foo
是真的C.__dict__['foo']
,但为了简单起见,我忽略了上面对类的描述符访问)。
也许一个具体的例子会有所帮助;这里有两个描述符,一个是数据描述符(有__set__
方法),另一个不是数据描述符:
>>> class DataDesc(object):
... def __get__(self, inst, type_):
... print('Accessed the data descriptor')
... return 'datadesc value'
... def __set__(self, inst, value):
... pass # just here to make this a data descriptor
...
>>> class OtherDesc(object):
... def __get__(self, inst, type_):
... print('Accessed the other, non-data descriptor')
... return 'otherdesc value'
...
>>> class C(object):
... def __init__(self):
... # set two instance attributes, direct access to not
... # trigger descriptors
... self.__dict__.update({
... 'datadesc': 'instance value for datadesc',
... 'otherdesc': 'instance value for otherdesc',
... })
... datadesc = DataDesc()
... otherdesc = OtherDesc()
...
>>> bar = C()
>>> bar.otherdesc # non-data descriptor, the instance wins
'instance value for otherdesc'
>>> bar.datadesc # data descriptor, the descriptor wins
Accessed the data descriptor
'datadesc value'
考虑以下代码片段:
class X:
@property
def x(self):
return 2
x1 = X()
x1.__dict__['x'] = 1
print(x1.x)
此代码打印 2,因为数据描述符(在类上定义)优先于实例字典。