Python的copy.deepcopy()通过memo字典(以id为键、副本为值)避免循环引用导致的无限递归,首次复制时存入副本,再次遇到同一对象ID则直接返回缓存副本;memo生命周期限于单次调用,结束后自动回收,无内存泄漏风险。

Python 的 copy.deepcopy() 能安全处理循环引用,核心就是用哈希表(即 memo 字典)做“已访问对象”的快速记录——它不靠值比较,而是用对象内存地址(id())作键,实现 O(1) 查找和防重入。
哈希表怎么记:用 id 当键,副本当值
每次准备复制一个对象前,先查 memo[id(obj)]:
- 如果命中,说明这个对象已被复制过,直接返回缓存的副本,跳过递归
- 如果未命中,就新建副本、存入
memo[id(obj)] = new_obj,再继续递归处理其子对象
这样,哪怕 a → b → a 形成环,第二次遇到 a 时也能立刻返回已有副本,彻底切断无限递归。
为什么必须用 id() 而不是对象本身作键
若把原对象(如自定义类实例)直接当字典键,会意外增加它的引用计数,干扰垃圾回收;而 id() 是整数,轻量、稳定、无副作用。备忘录只在单次深拷贝中存在,调用结束即被自动回收,不会泄漏。
手动模拟时的关键细节
写自己的深拷贝函数时,memo 参数必须设为 None 默认值,并在首次调用时初始化:
- 错:
def my_deepcopy(x, memo={})—— 可变默认参数,跨调用污染 - 对:
def my_deepcopy(x, memo=None): if memo is None: memo = {}
对容器类型(list/dict)要“先占位后填充”:比如复制列表时,先创建空列表并存进 memo,再逐项递归复制并追加——确保子对象在引用父对象时,父对象已在 memo 中可查。
验证是否生效的小技巧
构造带循环引用的数据结构,比如:
a = [1, 2]<br>b = [3, 4, a]<br>a.append(b)
用 sys.getrefcount() 或打印 id() 对比原对象与副本中对应位置的地址,能清晰看到:循环链在副本中被完整重建,但每个节点都是新对象,且 memo 在过程中只起“路标”作用,不残留。

















