5

我一直在阅读Descriptor HowTo Guide中的描述符,我对这句话感到困惑:

如果实例的字典中有一个与数据描述符同名的条目,则数据描述符优先。

字典如何包含两个具有相同名称的项目(一个普通条目和一个数据描述符)?或者是描述符的属性没有存储在__dict__

4

2 回答 2

6

数据描述符位于名称空间中,而实例属性位于实例名称空间中(so instance.__dict__)。这是两个独立的字典,所以这里没有冲突。

foo因此,对于实例名称的任何给定属性查找bar,Python 也会按以下顺序查看它的类(如下type(bar)命名C):

  1. C.foo被查找。如果它是一个数据描述符,这就是查找结束的地方。C.foo.__get__(bar, C)被退回。否则,Python 将存储此结果以供第 3 步使用(无需查找两次)。

  2. 如果C.foo不存在或者是常规属性,则 Python 会查找bar.__dict__['foo']. 如果存在,则返回。请注意,如果C.foo是数据描述符,则永远不会到达此部分!

  3. 如果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'
于 2016-11-07T11:45:23.573 回答
1

考虑以下代码片段:

class X:
    @property
    def x(self):
        return 2

x1 = X()
x1.__dict__['x'] = 1
print(x1.x)

此代码打印 2,因为数据描述符(在类上定义)优先于实例字典。

于 2016-11-07T11:47:25.067 回答