这里的官方文档描述了在形如a.b
的过程中都发生了什么:
首先会尝试a.__dict__['b']
,然后如果没找到,则type(a).__dict__['b']
,再没找到则顺着mro顺序逐个尝试,直到找到未知。且在找到之后查看这里的b
是否定义有描述器相关的方法__get__ / __set__
,如果有的话,则用描述器相关方法来取代默认行为。
有下面这样一个简单例子:
class Descri(object):
def __get__(self, obj, type=None):
print("call get")
def __set__(self, obj, value):
print("call set")
class A(object):
x = Descri()
a = A()
a.__dict__['x'] = 1
在最后一行执行完毕之后,我们获取a.x
。
按照前面说的查找顺序,应该直接在实例a
的__dict__
中就能够找到x
属性,且其等于1并且没有定义任何的特殊方法,那么按照查找优先级来看应该直接返回1,而且不应该继续查找任何的type(a).__dict__
才对。但是实际却不是这样,实际上依然调用了Descri.__get__
方法。
请问这是为什么?
谢邀,
a.x
在实例属性的变量空间就命中了,因此不会到上级的类的变量空间再查找了。在我这的测试,也并没有调用Descri.__get__
:本地的 3.6 版本也是一样的结果。
======分割线=====
根据题主说的在最后一行试试
a.x
,还真是执行了Descri.__get__
。这种诡异的调用顺序其实是在文档里就有的解释的。一个属性的访问已被描述符协议方法重写对象属性:
__get__()
,__set__()
,和__delete__()
。如果为对象定义了任何这些方法,则称其为描述符。一般情况下(我是说一般情况下),
a.x
的查找链是,先a.__dict__['x']
然后再是type(a).__dict__['x']
,若没找到则继续通过type(a)
的基类查找。但是但是,如果查找的值其中一个是定义了描述符方法的对象,那么 Python 会覆盖这个默认行为转而调用描述符的方法。这种行为也会因为调用的不同而稍有不一样:
a.x
则转换为调用: 。type(a).__dict__['x'].__get__(a, type(a))
A.x
则转换为:A.__dict__['x'].__get__(None, A)
更多参考可见文档:https://docs.python.org/3/ref...