__getattribute__
在发生属性访问时会被调用。
class Foo(object):
def __init__(self, a):
self.a = 1
def __getattribute__(self, attr):
try:
return self.__dict__[attr]
except KeyError:
return 'default'
f = Foo(1)
f.a
这将导致无限递归。罪魁祸首是return self.__dict__[attr]
这一行代码。我们假装(实际上这很接近事实),所有属性都存储在self.__dict__
中,并通过名称访问。
f.a
尝试访问
f
的
a
属性。这会调用
f.__getattribute__('a')
。然后
__getattribute__
尝试加载
self.__dict__
。
__dict__
是
self == f
的一个属性,因此Python调用
f.__getattribute__('__dict__')
,再次尝试访问属性
'__dict__'
。这是无限递归。
如果使用了
__getattr__
,则它永远不会运行,因为
f
具有
a
属性。如果它运行了(假设您要求
f.b
),则不会被调用以查找
__dict__
,因为它已经存在,只有在所有其他查找属性的方法失败时才会调用
__getattr__
。
使用
__getattribute__
编写上述类的“正确”方式是:
class Foo(object):
def __getattribute__(self, attr):
return super().__getattribute__(attr)
super().__getattribute__(attr)
将最近的超类(严格来说是在类的方法解析顺序或 MRO 中下一个类)的 __getattribute__
方法绑定到当前对象 self
上,然后调用它并让它完成工作。
使用 __getattr__
可以避免所有这些麻烦,它允许 Python 做它通常的事情,直到找不到属性为止。此时,Python 将控制权移交给你的__getattr__
方法,并让它想出一个解决方案。
值得注意的是,你也可能会在 __getattr__
中遇到无限递归问题。
class Foo(object):
def __getattr__(self, attr):
return self.attr
我会把那个留做一道练习题。