编写一个非数据描述符

6

我正在学习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__ 的这种行为吗?

1个回答

13
您成功创建了一个适当的非数据描述符,但是您通过设置实例属性来“掩盖”(mask)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 Datamodel参考文档中的调用描述符部分。


(*) 如果数据描述符实现了__get__钩子,则通过instance.attribute_name访问这样的描述符将返回描述符对象,除非'attribute_name'存在于instance.__dict__


如果只定义了 __set__,那会怎样?这是什么意思? - BAKE ZQ
@BAKEZQ:那么你有一个数据描述符,它不会钩入属性名称的读取访问。在这种情况下,你总是会得到描述符对象本身。 - Martijn Pieters
@BAKEZQ:请注意,这种不寻常的描述符实现不会覆盖实例上的属性访问。因此,如果描述符是Class.set_descriptor,而instanceClass的实例,则在访问instance.set_descriptor时,取决于instance.__dict__['set_descriptor']是否存在,以确定返回描述符还是实例属性值。 - Martijn Pieters
@BAKEZQ:是的,一个实现了__set__方法的描述符,在尝试设置属性名时总是会被调用。 - Martijn Pieters
数据描述符和非数据描述符在计算覆盖实例字典条目时有所不同。如果实例字典中有与数据描述符同名的条目,则数据描述符优先。如果实例字典中有与非数据描述符同名的条目,则字典条目优先。文档中没有为此语句提供任何示例,而上面的问题则是一个完美的例子! - Python Newbie
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接