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.x
到B.__dict__['x'].__get__(None, B)
'时,我希望输出是相同的。他们为什么不同?
答案 0 :(得分:2)
描述符how-to是一种简化。它掩盖了诸如元类之类的东西,以及类是对象的事实。类是对象,它们通过“对象样式” 和“类样式”属性查找和描述符处理进行。 (如果要独立验证,可以在type_getattro
中找到实现。)
对object.__class__
的查找不仅仅通过object.__mro__
;它还通过type(object).__mro__
进行查找。在type(object).__mro__
中找到的描述符使用“对象样式”描述符处理,将该类视为其元类的实例,而在object.__mro__
中找到的描述符使用“类样式”描述符处理。
当您查找object.__class__
时,Python会搜索type(object).__mro__
。由于object
在type(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__
一样,“对象样式”描述符处理也在这里发生。 (此外,如果您想知道的话,即使您不编写设置器,属性也是数据描述符。)