避免 __getattribute__ 中的无限循环 [英] Avoiding infinite loops in __getattribute__

查看:42
本文介绍了避免 __getattribute__ 中的无限循环的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

__getattribute__ 方法需要仔细编写,以避免死循环.例如:

The method __getattribute__ needs to be written carefully in order to avoid the infinite loop. For example:

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

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

Hence we need to write the method in this way:

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 类上的相同方法.为什么,那么通过超类调用方法不会导致无限循环?

My question is why does object.__getattribute__ method work? From where does object get the __getattribute__ method? And if object does not have any __getattribute__, then we are just calling the same method on class C but via the super class. Why, then calling the method via super class does not result in an infinite loop?

推荐答案

你似乎认为你的 __getattribute__ 实现只是一个钩子,如果你提供它,Python 会调用它, 否则解释器将直接执行正常的魔术.

You seem to be under the impression that your implementation of __getattribute__ is merely a hook, that if you provide it Python will call it, and otherwise the interpreter will do its normal magic directly.

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

That is not correct. When python looks up attributes on instances, __getattribute__ is the main entry for all attribute access, and object provides the default implementation. Your implementation is thus overriding the original, and if your implementation provides no alternative means of returning attributes it fails. You cannot use attribute access in that method, since all attribute access to the instance (self) is channelled again through type(self).__getattribute__(self, attr).

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

The best way around this is by calling the overridden original again. That's where super(C, self).__getattribute__(attr) comes in; you are asking the next class in the class-resolution order to take care of the attribute access for you.

或者,您可以直接调用未绑定的 object.__getattribute__() 方法.此方法的 C 实现是属性访问的最后一站(它可以直接访问 __dict__,因此不受相同限制).

Alternatively, you can call the unbound object.__getattribute__() method directly. The C implementation of this method is the final stop for attribute access (it has direct access to __dict__ and is thus not bound to the same limitations).

请注意,super() 返回一个代理对象,该对象将在方法解析排序的基类中查找接下来可以找到的任何方法.如果不存在这样的方法,它将因属性错误而失败.它将永远调用原始方法.因此,Foo.bar() 查找 super(Foo, self).bar 要么是基类实现要么是属性错误,永远不会 Foo.bar 本身.

Note that super() returns a proxy object that'll look up whatever method can be found next in the method-resolution ordered base classes. If no such method exists, it'll fail with an attribute error. It will never call the original method. Thus Foo.bar() looking up super(Foo, self).bar will either be a base-class implementation or an attribute error, never Foo.bar itself.

这篇关于避免 __getattribute__ 中的无限循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆