编写非数据描述符


问题内容

我正在学习python中的描述符。我想编写一个非数据描述符,但是以描述符为类__get__方法的类在调用类方法时不会调用特殊方法。这是我的示例(没有__set__):

class D(object):

    "The Descriptor"

    def __init__(self, x = 1395):
        self.x = x

    def __get__(self, instance, owner):
        print "getting", self.x
        return self.x


class C(object):

    d = D()

    def __init__(self, d):
        self.d = d

这就是我所说的:

>>> c = C(4)
>>> c.d
4

__get__描述符类的就没有得到调用。但是当我还设置一个__set__描述符时,它似乎被激活了:

class D(object):

"The Descriptor"

    def __init__(self, x = 1395):
        self.x = x

    def __get__(self, instance, owner):
        print "getting", self.x
        return self.x

    def __set__(self, instance, value):
        print "setting", self.x
        self.x = value

class C(object):

    d = D()

    def __init__(self, d):
        self.d = d

现在,我创建一个C实例:

>>> c=C(4)
setting 1395
>>> c.d
getting 4
4

并且两者__get__, __set__都存在。似乎我缺少关于描述符及其使用方法的一些基本概念。谁能解释这种行为__get__, __set__


问题答案:

您已经成功创建了正确的非数据描述符,但是随后通过设置实例属性来 屏蔽d属性。

因为它是一个
数据描述符,所以实例属性在这种情况下会获胜。添加__set__方法时,将描述符变成数据描述符,并且即使存在实例属性,也始终应用数据描述符。(*)

描述符方法

属性访问的默认行为是从对象的字典中获取,设置或删除属性。例如,a.x有一个查找链,从a.__dict__['x'],然后到type(a).__dict__['x'],并一直到type(a)排除元类的基类。如果查找到的值是定义描述符方法之一的对象,则Python可能会覆盖默认行为并改为调用描述符方法。优先级链在何处发生取决于定义了哪些描述符方法。

如果对象同时定义__get__()__set__(),则将其视为数据描述符。仅定义__get__()的描述符称为非数据描述符(它们通常用于方法,但也可以用于其他用途)。

数据和非数据描述符的不同之处在于,如何计算实例字典中条目的替代值。如果实例的字典具有与数据描述符同名的条目,则数据描述符优先。如果实例的字典具有与非数据描述符同名的条目,则该字典条目优先。

如果您 删除d实例属性(从来没有对它进行设置或从实例中删除),描述对象被调用:

>>> class D(object):
...     def __init__(self, x = 1395):
...         self.x = x
...     def __get__(self, instance, owner):
...         print "getting", self.x
...         return self.x
...
>>> class C(object):
...     d = D()
...
>>> c = C()
>>> c.d
getting 1395
1395

再次添加一个实例属性,由于实例属性获胜,描述符将被忽略:

>>> c.d = 42  # setting an instance attribute
>>> c.d
42
>>> del c.d   # deleting it again
>>> c.d
getting 1395
1395

另请参见Python数据 模型 参考中的 调用描述符
文档

__


(*)如果数据描述符实现了__get__挂钩。 除非 存在于中, 否则
通过访问此类描述符instance.attribute_name将返回描述符对象。
'attribute_name'``instance.__dict__