Python:避免__getattribute__中的无限循环

tre*_*der 25 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
Run Code Online (Sandbox Code Playgroud)

因此我们需要以这种方式编写方法:

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)
Run Code Online (Sandbox Code Playgroud)

我的问题是为什么object.__getattribute__方法有效?从何处object获取__getattribute__方法?如果object没有__getattribute__,那么我们只是在类上调用相同的方法,C但是通过超类.为什么,然后通过超类调用方法不会导致无限循环?

Mar*_*ers 28

你似乎有这样一种印象,你的执行下__getattribute__仅仅是一个钩,如果你提供给它的Python会调用它,否则解释器将直接完成其正常的魔力.

这是不正确的.当python在实例上查找属性时,__getattribute__是所有属性访问的主条目,并object提供默认实现.因此,您的实现将覆盖原始实现,如果您的实现没有提供返回属性的替代方法,则它将失败.您不能在该方法中使用属性访问,因为对instance(self)的所有属性访问都会再次通过type(self).__getattribute__(self, attr).

解决这个问题的最佳方法是再次调用被覆盖的原件.那是super(C, self).__getattribute__(attr)进来的地方; 您要求类解析顺序中的下一个类为您处理属性访问.

或者,您可以object.__getattribute__()直接调用未绑定的方法.此方法的C实现是属性访问的最终停止(它具有直接访问权限,__dict__因此不受相同限制的约束).

请注意,super()返回一个代理对象,该对象将查找方法分辨率有序基类中接下来可以找到的任何方法.如果不存在这样的方法,它将失败并出现属性错误.它永远不会调用原始方法.因此Foo.bar()查找super(Foo, self).bar将是基类实现或属性错误,从不 Foo.bar自己.


Vau*_*ato 9

当你这样做:

    return object.__getattribute__(self, x)
Run Code Online (Sandbox Code Playgroud)

你正在调用一个特定的函数 - 在对象类中定义的函数,而不是在A中定义的函数,因此没有递归.

当你这样做:

    return self.x
Run Code Online (Sandbox Code Playgroud)

你让python选择要调用哪个函数,并从A中调用一个函数,并且你有一个无限递归.


tst*_*pko 5

这样写(C继承自object):

class C(object):
   def __init__(self):
       self.x = 100

   def __getattribute__(self, x):
       return object.__getattribute__(self, x)
Run Code Online (Sandbox Code Playgroud)

现在您了解object .__ getattribute __(self,x)起作用的原因-您正在调用父对象。