在Python面向对象编程中,自定义类的属性访问逻辑时,如果处理不当很容易出现属性递归调用,最终触发RecursionError错误。这种错误本质是属性访问的调用链没有终止条件,导致递归深度超过Python解释器的默认限制。

属性递归调用导致RecursionError的核心原理
Python中类的属性访问有固定的调用流程,当我们访问一个实例的属性时,解释器会先查找实例的__dict__字典,如果没找到,就会调用类的__getattr__方法;而设置属性时,默认会调用__setattr__方法。如果我们在重写这些方法时,内部又触发了同类型的属性访问或设置操作,就会形成无限递归。
Python解释器默认的递归深度限制是1000层,当递归调用超过这个限制时,就会抛出RecursionError,提示递归深度超出最大值。比如下面这个错误的__getattr__重写示例:
class TestClass:
def __getattr__(self, name):
# 这里访问self.name会再次触发__getattr__调用,形成无限递归
return self.name
obj = TestClass()
# 访问不存在的属性时触发错误
print(obj.test_attr)
运行上述代码时,访问obj.test_attr会调用__getattr__,方法内部又访问self.name,再次触发__getattr__,循环往复直到递归深度超限。
常见的错误触发场景
1. 重写__setattr__时未正确使用实例字典
__setattr__方法用于设置实例属性,如果重写时直接通过self.name = value的方式赋值,会再次触发__setattr__调用,导致递归:
class BadSetClass:
def __setattr__(self, name, value):
# 直接赋值会再次调用__setattr__,形成无限递归
self.name = value
obj = BadSetClass()
# 设置属性时触发错误
obj.attr = 123
2. __getattr__和__setattr__互相触发
有些场景下__getattr__内部调用了属性设置,而__setattr__内部又调用了属性访问,两者互相触发也会导致递归:
class MutualTriggerClass:
def __getattr__(self, name):
# 访问属性时触发设置属性
self.temp = 1
return getattr(self, name)
def __setattr__(self, name, value):
# 设置属性时触发访问属性
print(getattr(self, name))
super().__setattr__(name, value)
obj = MutualTriggerClass()
obj.test = 1
对应的修复方案
1. 使用实例的__dict__字典直接操作属性
重写__setattr__时,不要直接通过self.name = value赋值,而是直接操作实例的__dict__字典,避免再次触发__setattr__:
class FixSetClass:
def __setattr__(self, name, value):
# 直接操作实例的__dict__,不会触发__setattr__递归
self.__dict__[name] = value
obj = FixSetClass()
obj.attr = 123
print(obj.attr) # 输出:123
2. 调用父类方法实现属性访问/设置
对于__getattr__方法,如果需要访问实例已有的属性,应该通过父类的__getattribute__方法获取,而不是直接访问属性:
class FixGetClass:
def __init__(self):
self.exist_attr = "已有属性"
def __getattr__(self, name):
# 访问存在的属性时通过父类方法获取,避免递归
try:
return super().__getattribute__(name)
except AttributeError:
return f"{name} 属性不存在"
obj = FixGetClass()
print(obj.exist_attr) # 输出:已有属性
print(obj.no_exist) # 输出:no_exist 属性不存在
3. 添加递归终止条件
如果确实需要在__getattr__或__setattr__内部做属性操作,可以添加明确的终止条件,避免无限递归:
class TerminateClass:
def __init__(self):
self._internal_flag = False
def __getattr__(self, name):
if self._internal_flag:
raise AttributeError(f"{name} 属性不存在")
self._internal_flag = True
# 模拟一次属性访问后终止
result = f"处理{name}属性"
self._internal_flag = False
return result
obj = TerminateClass()
print(obj.test) # 输出:处理test属性
4. 临时调整递归深度限制(不推荐作为根本解决方案)
如果确实需要较深的递归逻辑,可以通过sys模块临时调整递归深度限制,但这只是临时方案,根本还是要修复递归逻辑:
import sys # 获取当前递归深度限制 print(sys.getrecursionlimit()) # 默认输出:1000 # 临时调整为2000 sys.setrecursionlimit(2000)
总结
属性递归调用导致的RecursionError本质是属性访问/设置的调用链没有终止条件,解决这类问题的核心是避免重写属性方法时触发自身的无限调用。优先使用直接操作__dict__或者调用父类内置方法的方式实现属性逻辑,同时尽量减少在__getattr__和__setattr__内部做复杂的属性操作,从根源上避免递归问题的出现。
PythonRecursionError属性递归调用__getattr____setattr__修改时间:2026-07-12 11:03:28