Python:避免__getattribute__中的无限循环

时间:2012-11-24 04:39:06

标签: python python-3.x

需要仔细编写方法__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上调用相同的方法,但是通过超类。为什么,然后通过超类调用方法不会导致无限循环?

3 个答案:

答案 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)有效 - 你正在调用父对象。