【问题标题】:Writing a Non-Data Descriptor编写非数据描述符
【发布时间】:2016-09-10 11:38:25
【问题描述】:

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

【问题讨论】:

    标签: python python-2.7 descriptor python-descriptors


    【解决方案1】:

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

    因为它是一个-数据描述符,所以在这种情况下实例属性胜出。当你添加一个__set__ 方法时,你把你的描述符变成了一个数据描述符,并且数据描述符总是被应用,即使有一个实例属性。 (*)

    来自Descriptor Howto

    属性访问的默认行为是从对象的字典中获取、设置或删除属性。例如,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 参考中的 Invoking Descriptors documentation


    (*) 前提是数据描述符实现了__get__ 挂钩。通过instance.attribute_name访问这样的描述符将返回描述符对象除非'attribute_name'存在于instance.__dict__

    【讨论】:

    • 如果只定义__set__ 会怎样。这是什么?
    • @BAKEZQ:那么你就有了一个数据描述符,它不会挂钩对属性名称的读取访问。在这种情况下,您将始终获得描述符对象本身。
    • @BAKEZQ:考虑到这种不寻常的描述符实现不会覆盖实例上的属性访问,所以如果描述符是Class.set_descriptor 并且instanceClass 的实例,那么访问instance.set_descriptor时是否返回描述符或实例属性值取决于instance.__dict__['set_descriptor']是否存在。
    • 作业怎么样。似乎描述符总是覆盖__dict__中的属性(我的意思是它总是调用__set__
    • @BAKEZQ:是的,实现__set__ 的描述符在尝试设置属性名称时总是被调用。
    猜你喜欢
    • 2012-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-16
    • 1970-01-01
    相关资源
    最近更新 更多