29

该方法__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但通过超类。为什么,那么通过超类调用方法不会导致无限循环?

4

3 回答 3

36

您似乎认为您的实现__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本身。

于 2012-11-24T05:03:58.630 回答
10

当你这样做时:

    return object.__getattribute__(self, x)

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

当你这样做时:

    return self.x

你让python选择调用哪个函数,它从A调用那个函数,你有一个无限递归。

于 2012-11-24T04:45:02.123 回答
5

像这样写(C继承自object):

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

   def __getattribute__(self, x):
       return object.__getattribute__(self, x)

现在你明白了为什么 object.__getattribute__(self, x) 有效——你正在调用父对象。

于 2013-11-04T10:03:54.137 回答