了解 "descriptors howto" 文章中的描述符示例

Understanding the descriptor example from "descriptors howto" article

我正在努力研究描述符的概念,但最近没有成功。这个 article 的描述符 How to 确实帮助了我,同时也让我感到困惑。我在这里为这个例子苦苦挣扎 为什么 m.x 调用 def __get__(self, obj, objtype):

class RevealAccess(object):
    def __init__(self, initval=None, name='var'):
        self.val = initval
        self.name = name

    def __get__(self, obj, objtype):
        print('Retrieving', self.name)
        return self.val

    def __set__(self, obj, val):
        print('Updating', self.name)
        self.val = val

>>> class MyClass(object):
...     x = RevealAccess(10, 'var "x"')
...     y = 5
...
>>> m = MyClass()
>>> m.x
Retrieving var "x"
10

我相信我为此苦苦挣扎的原因是因为我对文章中的以下陈述感到困惑

The details of invocation depend on whether obj is an object or a class. For objects, the machinery is in object.__getattribute__() which transforms b.x into type(b).__dict__['x'].__get__(b, type(b)). The implementation works through a precedence chain that gives data descriptors priority over instance variables, instance variables priority over non-data descriptors, and assigns lowest priority to __getattr__() if provided

我不确定作者在这里所说的对象或 class 是什么意思。 object 是否表示 class 的实例?我知道当我们做 instance.var python 时,内部会做 instance.__dict__["var"],如果没有找到,那么它会做 class.__dict__["var"]。在那个概念之后我有点迷失了它。谁能解释一下这个例子是如何工作的。如何用 m.x 调用定义 def __get__(self, obj, objtype):。如果有人能解决这个问题,我将不胜感激。

是的,对象作者的意思是一个实例,而不是class。区别来自描述符所在的位置;描述符是在 class 上定义的,因此当直接在 class 上访问该描述符 时,与在class.

在您的示例中,m 是一个实例。该实例本身没有属性 m'x' in m.__dict__False)。 Python 还查看 type(m) 来解析该属性,type(m) 这里是 MyClass'x' in MyClass.__dict__True,并且 MyClass.__dict__['x'].__get__ 存在,所以 Python 现在知道那个对象是一个描述符。

所以,因为没有m.__dict__['x'],但是一个type(m).__dict__['x'].__get__,那个方法用mtype(m) 作为参数,导致 type(m).__dict__['x'].__get__(m, type(m))

如果 'x' in MyClass.__dict__ 为真但 MyClass.__dict__['x'].__get__ 存在(因此该对象 是描述符object), 则直接返回 MyClass.__dict__['x'].

如果您尝试将 x 属性也添加到实例 ,情况会变得更加有趣。在那种情况下,MyClass.__dict__['x'].__set__MyClass.__dict__['x'].__delete__ 是否存在很重要,使描述符成为 data 描述符。在平局的情况下,数据描述符总是获胜。所以ifMyClass.__dict__['x'].__get__至少MyClass.__dict__['x'].__set__MyClass.__dict__['x'].__delete__之一存在,没关系如果 m.__dict__['x'] 也存在,则不再存在。 Python 甚至都不会去寻找它。

但是,如果 class 上的描述符对象没有 __set____delete__ 方法,则 m.__dict__['x'] 获胜并返回。在那种情况下,描述符是一个常规的非数据描述符,它输给了实例属性。

最后但同样重要的是,如果 'x' in type(m).__dict__ 为假(class 上没有对象),则返回 m.__dict__['m']。描述符协议仅适用于在 class 上找到的对象,从不适用于实例上的属性。

Does object mean an instance of a class ?

是的。

How is the definition def __get__(self, obj, objtype): being called with m.x.

因为您引用的文档中说的是:

For objects, the machinery is in object.__getattribute__() which transforms b.x into type(b).__dict__['x'].__get__(b, type(b)).

object.__getattribute__ 是一种机制,可实现当您执行类似 m.x 时所发生的事情。所以,明确地:

m.x
type(m).__dict__['x'].__get__(m, type(m))
MyClass.__dict__['x'].__get__(m, MyClass)
RevealAccess(10, 'var "x"').__get__(m, MyClass)

所以最后一行调用了RevealAccessclass的__get__方法。