copy.deepcopy() 并非万能:对循环引用、不可序列化对象(如文件句柄、线程锁)、未正确实现__reduce__/__getstate__的自定义类,以及旧版numpy数组视图,可能静默降级、抛typeerror或性能骤降。

直接用 copy.deepcopy() 就行,但得清楚它在什么情况下会失效、慢、或抛错。
为什么 copy.deepcopy() 有时不生效?
它无法处理循环引用、不可序列化的对象(比如文件句柄、线程锁、Lambda 函数)、以及某些 C 扩展类(如 numpy.ndarray 的部分视图)。
- 遇到
TypeError: cannot pickle '_io.TextIOWrapper' object,说明对象里混了打开的文件 - 含
threading.Lock()的类实例会直接报TypeError: can't pickle _thread.lock objects - 自定义类没定义
__reduce__或__getstate__,且含不可拷贝字段时,可能静默丢数据
哪些场景下该换方案而不是硬扛 deepcopy?
当目标是“避免后续篡改”,不一定非得深拷贝——有时更轻量、更可控。
- 只读需求:用
types.MappingProxyType(dict)封装字典,或用tuple替代list,从源头禁止修改 - 结构简单且固定:手动构造新对象,比如
{k: v.copy() for k, v in old_dict.items()}(仅一层嵌套) - 含 NumPy 数组:优先用
arr.copy()而非deepcopy,后者可能复制元数据失败或极慢 - 需要跨进程/网络传输:改用
pickle.dumps()+pickle.loads(),二者行为更一致
deepcopy 的性能陷阱和替代写法
对超大嵌套结构(比如上万级嵌套字典),deepcopy 会递归建栈、反复查 ID 去重,极易触发 RecursionError 或吃光内存。
- 调大递归限制治标不治本:
sys.setrecursionlimit(50000)可能导致解释器崩溃 - 用
json.loads(json.dumps(obj))可绕过循环引用问题,但只支持 JSON 基元类型(丢函数、日期、自定义类) - 对纯数据容器,
ast.literal_eval(repr(obj))更安全,但repr不保证可逆(比如浮点精度、NaN 表示) - 真正要保类型+结构+性能?得自己写迭代式深拷贝,用栈模拟递归,显式维护已拷贝对象映射
最常被忽略的是:深拷贝解决不了逻辑层的数据污染。比如两个变量指向同一份配置字典,你改了其中一份的某个子键,另一份是否受影响,取决于拷贝时机和嵌套深度——不是调了 deepcopy 就万事大吉。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











