Python中如何避免__getattr__方法陷入死循环的问题

class Proxy:
def init(self, obj):
# self._obj = obj,则调用__getattr__就会死循环,而改成 self._obj = obj,则不会;
self.obj = obj

def getattr(self, item):
return getattr(self.obj, item)


class Spam:
def init(self, x):
self.x = x

def bar(self, y):
print(‘Spam.bar:’, self.x, y)


if name == “main”:
spam = Spam(10.05)
proxy = Proxy(spam)
print(proxy.x)

仅仅是将 Proxy 实例属性由_obj 的成 obj,就会造成__getattr__无限循环,这个怎么理解?谢谢。
Python中如何避免__getattr__方法陷入死循环的问题


4 回复

不知道你的环境是什么样的,使用你的代码跑了一下,没发现什么问题。


在Python里,__getattr__方法只在属性查找失败时被调用。但如果在这个方法内部又去访问一个不存在的属性,就会再次触发__getattr__,导致无限递归。要解决这个问题,核心是确保在__getattr__内部进行属性访问时,能明确地“绕开”它自身的触发机制。

最直接有效的方法是利用object.__getattribute__或者super().__getattribute__。这两个方法会直接调用默认的属性查找逻辑,而不会再次进入你自定义的__getattr__

下面是一个典型的错误示例和修正后的代码:

错误示例(会导致死循环):

class BadExample:
    def __getattr__(self, name):
        # 错误:这里尝试访问 self.data,但data属性不存在。
        # 这会导致再次调用 __getattr__('data'),陷入死循环。
        return self.data.get(name)

正确做法:

class SafeExample:
    def __init__(self):
        # 使用 object.__setattr__ 来绕过可能的 __setattr__ 重写
        object.__setattr__(self, '_data', {'foo': 'bar'})

    def __getattr__(self, name):
        # 关键:使用 object.__getattribute__ 来安全地访问内部属性 _data
        # 这不会再次触发 __getattr__
        internal_data = object.__getattribute__(self, '_data')
        if name in internal_data:
            return internal_data[name]
        raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")

# 测试
obj = SafeExample()
print(obj.foo)  # 输出: bar
print(obj.baz)  # 触发 AttributeError

代码解释:

  1. __init__中,我们使用object.__setattr__来设置一个内部字典_data。这是为了避免如果类中还重写了__setattr__可能带来的副作用。
  2. __getattr__内部,我们使用object.__getattribute__(self, '_data')来获取这个内部字典。这个调用直接使用了对象最基础的属性获取机制,确保了它不会回头来调用我们正在写的这个__getattr__方法。
  3. 然后我们检查请求的name是否在字典中,如果在就返回,否则抛出标准的AttributeError

总结一下:__getattr__里访问其他属性时,用object.__getattribute__()super().__getattribute__()来安全地获取。

哈哈,楼主肯定是在__init__初始化函数改了,忘记在 getattr 里面修改了,导致在获取的时候无线循环一直到报 error
<script src=“https://gist.github.com/zhusimaji/df879dd537a6729b6201f456e2c08b71.js”></script>

谢谢二位,原因找到了!有一段__setattr__的代码没贴出来,在__setattr__中调用了__getattr__,没做一些拦截处理,造成重复调用__getattr__,话说,这几个魔术方法真是死循环的高危险操作呢

回到顶部