用Python编写非数据描述符

4 python descriptor python-2.7 python-descriptors

我正在学习python中的描述符.我想编写一个非数据描述符,但是__get__当我调用classmethod时,具有描述符作为其类方法的类不会调用特殊方法.这是我的例子(没有__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
Run Code Online (Sandbox Code Playgroud)

以下是我称之为:

>>> c = C(4)
>>> c.d
4
Run Code Online (Sandbox Code Playgroud)

__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
Run Code Online (Sandbox Code Playgroud)

现在我创建一个C实例:

>>> c=C(4)
setting 1395
>>> c.d
getting 4
4
Run Code Online (Sandbox Code Playgroud)

两者__get__, __set__都存在.似乎我缺少一些关于描述符的基本概念以及如何使用它们.任何人都可以解释这种行为__get__, __set__吗?

Mar*_*ers 9

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

因为它是非数据描述符,所以实例属性在这种情况下获胜.添加__set__方法时,将描述符转换为数据描述符,即使存在实例属性,也始终应用数据描述符.

描述符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
Run Code Online (Sandbox Code Playgroud)

再次添加实例属性并忽略描述符,因为实例属性获胜:

>>> c.d = 42  # setting an instance attribute
>>> c.d
42
>>> del c.d   # deleting it again
>>> c.d
getting 1395
1395
Run Code Online (Sandbox Code Playgroud)

另请参阅Python Datamodel参考中的Invoking Descriptors文档.