5

我正在学习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__

4

1 回答 1

12

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

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

描述符 Howto

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

如果一个对象同时定义了__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__.

于 2016-09-10T11:41:44.113 回答