需要仔细编写方法__getattribute__
以避免无限循环。例如:
class A:
def __init__(self):
self.x = 100
def __getattribute__(self, x):
return self.x
>>> a = A()
>>> a.x # infinite looop
RuntimeError: maximum recursion depth exceeded while calling a Python object
class B:
def __init__(self):
self.x = 100
def __getattribute__(self, x):
return self.__dict__[x]
>>> b = B()
>>> b.x # infinite looop
RuntimeError: maximum recursion depth exceeded while calling a Python object
因此我们需要以这种方式编写方法:
class C:
def __init__(self):
self.x = 100
def __getattribute__(self, x):
# 1. error
# AttributeError: type object 'object' has no attribute '__getattr__'
# return object.__getattr__(self, x)
# 2. works
return object.__getattribute__(self, x)
# 3. works too
# return super().__getattribute__(x)
我的问题是为什么object.__getattribute__
方法有效?从object
获取__getattribute__
方法的位置?如果object
没有任何__getattribute__
,那么我们只是在类C
上调用相同的方法,但是通过超类。为什么,然后通过超类调用方法不会导致无限循环?
答案 0 :(得分:26)
你似乎认为你的__getattribute__
的实现仅仅是一个钩子,如果你提供它,Python会调用它,否则解释器将直接执行它的正常魔法。
这是不正确的。当python在实例上查找属性时,__getattribute__
是所有属性访问的主条目,object
提供默认实现。因此,您的实现覆盖原始文件,如果您的实现没有提供返回属性的替代方法,则它将失败。您无法在该方法中使用属性访问权限,因为对实例(self
)的所有属性访问都会通过type(self).__getattribute__(self, attr)
再次引导。
解决这个问题的最佳方法是再次调用被覆盖的原件。这就是super(C, self).__getattribute__(attr)
进来的地方;您要求类解析顺序中的下一个课程为您处理属性访问。
或者,您可以直接调用未绑定的object.__getattribute__()
方法。此方法的C实现是属性访问的最后一站(它可以直接访问__dict__
,因此不受相同的限制)。
请注意,super()
返回一个代理对象,该对象将查找方法分辨率有序基类中接下来可以找到的任何方法。如果不存在这样的方法,它将失败并出现属性错误。它永远不会调用原始方法。因此Foo.bar()
查找super(Foo, self).bar
将是基类实现或属性错误,从不 Foo.bar
本身。
答案 1 :(得分:9)
执行此操作时:
return object.__getattribute__(self, x)
你正在调用一个特定的函数 - 在对象类中定义的函数,而不是在A中定义的函数,因此没有递归。
执行此操作时:
return self.x
你让python选择要调用哪个函数,它从A中调用一个函数,并且你有一个无限递归。
答案 2 :(得分:5)
像这样写( C 继承自对象):
class C(object):
def __init__(self):
self.x = 100
def __getattribute__(self, x):
return object.__getattribute__(self, x)
现在你明白了为什么object .__ getattribute __(self,x)有效 - 你正在调用父对象。