python的copy.deepcopy内置循环引用检测机制,通过memo字典记录已拷贝对象id以避免重复递归;自定义__deepcopy__时必须接收并传递memo参数,否则将绕过保护导致栈溢出。

克隆对象时遇到循环引用引发的栈溢出(RecursionError: maximum recursion depth exceeded 或 Fatal Python error: Cannot recover from stack overflow),本质是深拷贝过程反复进入同一对象链,无限压栈。这不是“复制失败”,而是Python默认的copy.deepcopy在遍历嵌套结构时,对已见过的对象缺乏缓存识别,导致死循环调用。
确认是否为循环引用触发
先验证问题根源:打印对象结构或使用id()观察引用关系。例如:
若手动遍历该对象会无限深入(如obj.a.b.c.a.b...),基本可判定是循环引用。
优先用内置deepcopy自动处理
Python标准库的copy.deepcopy本身已内置循环引用检测机制——它内部维护一个memo字典,记录已拷贝对象的id与对应副本。只要不手动破坏该逻辑,绝大多数循环引用都能安全处理。
确保你没做以下操作:
- 重写了
__reduce__或__getstate__但未正确处理引用闭环 - 在自定义
__deepcopy__方法中直接递归调用copy.deepcopy而未传入memo - 用
eval(repr(obj))或手工递归函数替代deepcopy
✅ 正确做法就是直接调用:import copy; new_obj = copy.deepcopy(original_obj)。
自定义拷贝逻辑时必须传memo参数
如果你为类实现了__deepcopy__,务必接收并传递memo参数,否则会绕过内置保护:
def __deepcopy__(self, memo):
# ✅ 正确:新建实例后,用memo注册,再逐字段深拷
new_obj = self.__class__.__new__(self.__class__)
memo[id(self)] = new_obj # 提前注册,避免后续重复拷贝
new_obj.parent = copy.deepcopy(self.parent, memo) # 传memo进去
new_obj.children = copy.deepcopy(self.children, memo)
return new_obj
❌ 错误写法:copy.deepcopy(self.parent)(漏掉memo)会导致二次进入,栈爆炸。
极端情况:禁用循环检测或改用序列化绕行
极少数场景(如含不可哈希对象、自定义C扩展对象),deepcopy的memo可能失效。此时可考虑:
- 用
json.dumps(obj, default=str) → json.loads()转成字典再重建(丢失方法和类型,但彻底切断引用) - 借助
dill库(比pickle更强,支持更多对象类型,自带循环引用处理) - 手动剪枝:预先遍历标记循环节点,拷贝时跳过反向引用字段(适用于业务逻辑明确的结构)
不建议强行调高sys.setrecursionlimit——它治标不治本,且可能掩盖真正设计缺陷。











