__get__ of descriptor __class__ of object class doesn't return as expected

时间:2019-03-23 08:51:20

标签: python python-3.x

I try to understand more thoroughly descriptor and explicit attribute name look-up order.
I read
descriptor howto的Onclick属性中,其状态如下:

  

调用的详细信息取决于obj是对象还是类:
      ...
  对于类,机制位于type.__getattribute__()中,它将B.x转换为B.__dict__['x'].__get__(None, B)

我在__class_ _上进行了测试,因为它是object的数据描述符

In [47]: object.__class__
Out[47]: type   

因此,由于type类创建了包括type类在内的所有类,因此它将按预期返回object。根据“描述符操作方法”,object.__class__变成了object.__dict__['__class__'].__get__(None, object)
但是,当我运行它时,输出是描述符本身,而不是type

In [48]: object.__dict__['__class__'].__get__(None, object)
Out[48]: <attribute '__class__' of 'object' objects>    

我猜想它返回描述符本身,因为在__get__内部有类似以下代码的代码:

if instance is None:
    return self    

因此,我了解从类调用时返回描述符本身的原因。令我感到困惑的是不同的输出

当它说'B.xB.__dict__['x'].__get__(None, B)'时,我希望输出是相同的。他们为什么不同?

1 个答案:

答案 0 :(得分:2)

描述符how-to是一种简化。它掩盖了诸如元类之类的东西,以及类是对象的事实。类是对象,它们通过“对象样式” “类样式”属性查找和描述符处理进行。 (如果要独立验证,可以在type_getattro中找到实现。)

object.__class__的查找不仅仅通过object.__mro__;它还通过type(object).__mro__进行查找。在type(object).__mro__中找到的描述符使用“对象样式”描述符处理,将该类视为其元类的实例,而在object.__mro__中找到的描述符使用“类样式”描述符处理。

当您查找object.__class__时,Python会搜索type(object).__mro__。由于objecttype(object).__mro__中,因此此搜索将找到object.__dict__['__class__']。由于object.__dict__['__class__']数据描述符(它具有__set__),因此它优先于通过object.__mro__进行的搜索。因此,将object视为object的实例而不是类,Python会执行

descr.__get__(object, type(object))

代替

descr.__get__(None, object)

__get__调用返回type(object),即type

您的手册descr.__get__(None, object)调用将object视为类,而不是object的实例。通过这种方式调用,描述符将返回自身。


为了证明__class__在这里不是特殊情况,我们可以创建自己的实例的 own 类,就像object是这样:

class DummyMeta(type):
    pass

class SelfMeta(type, metaclass=DummyMeta):
    @property
    def x(self):
        return 3

SelfMeta.__class__ = SelfMeta

print(SelfMeta.x)
print(SelfMeta.__dict__['x'].__get__(None, SelfMeta))
print(SelfMeta.__dict__['x'].__get__(SelfMeta, type(SelfMeta)))

输出:

3
<property object at 0x2aff9f04c5e8>
3

就像object.__class__一样,“对象样式”描述符处理也在这里发生。 (此外,如果您想知道的话,即使您不编写设置器,属性也是数据描述符。)