结论是用copy.deepcopy(),但需理解其默认不调用__init__、不触发初始化逻辑,且对不可序列化对象(如锁)会报错;可靠方式是显式实现__deepcopy__方法,手动控制各属性的拷贝策略。

直接说结论:用 copy.deepcopy(),但必须理解它对类实例的默认行为——它会递归拷贝所有属性,但不会调用 __init__(),也不会触发自定义初始化逻辑。
为什么 copy.deepcopy() 有时不按预期工作?
常见错误现象是:拷贝后对象看似独立,但修改嵌套可变对象(如列表、字典、自定义类实例)时仍相互影响;或者拷贝后丢失了某些状态(比如文件句柄、线程锁、缓存属性)。
根本原因是:copy.deepcopy() 默认通过 __getstate__() 和 __setstate__()(如果存在)或直接操作 __dict__ 来重建对象,它不走构造函数流程,也不感知业务语义。
- 若类中包含不可序列化对象(如
threading.Lock),会抛出TypeError: can't pickle _thread.lock objects - 若重写了
__getstate__()但没过滤掉非持久化字段,拷贝可能失败或带入脏状态 - 若依赖
__init__()中的副作用(如注册回调、打开连接),deepcopy 后这些不会重放
如何让自定义类支持可靠深度拷贝?
关键不是“怎么调用 deepcopy”,而是“怎么设计类让它能被安全 deep-copied”。最稳妥的方式是显式实现 __deepcopy__() 方法。
立即学习“Python免费学习笔记(深入)”;
该方法接收两个参数:self 和 memo(用于处理循环引用)。你需要手动决定哪些属性要深拷、哪些浅拷、哪些跳过。
import copy
<p>class Config:
def <strong>init</strong>(self, data, cache=None):
self.data = data # 可变,需深拷
self.cache = cache or {}
self._lock = threading.Lock() # 不可拷,应忽略</p><pre class="brush:php;toolbar:false;">def __deepcopy__(self, memo):
# 创建新实例,不调用 __init__
new_obj = Config.__new__(Config)
# 深拷关键数据
new_obj.data = copy.deepcopy(self.data, memo)
new_obj.cache = copy.deepcopy(self.cache, memo)
# 忽略不可拷对象,或重新初始化
new_obj._lock = threading.Lock()
return new_obj
- 务必用
__new__()创建空实例,避免触发__init__() - 对每个属性显式调用
copy.deepcopy(..., memo),传入memo防止无限递归 - 对资源型字段(锁、文件描述符、socket)不要拷,应重新创建或设为
None
copy.deepcopy() 的性能和兼容性要注意什么?
它在内部维护一个 memo 字典记录已拷贝对象 ID,对含大量重复引用或深层嵌套结构的类,开销明显上升;对有循环引用的对象(如树节点互相持有 parent/children),不实现 __deepcopy__() 可能直接报错或卡死。
- 避免在热路径(如高频请求处理)中 deep-copy 大型配置对象,考虑用不可变数据结构替代
- Python 3.8+ 对
dataclass类支持自动__deepcopy__,但仅限于标准字段;含自定义逻辑仍需手动覆盖 - 若类继承自内置类型(如
list或dict),deepcopy行为由父类控制,子类的额外属性可能被忽略,除非也实现__deepcopy__
真正难的不是写那行 copy.deepcopy(obj),而是想清楚:这个对象里哪些状态是“值”,哪些是“资源”,哪些压根不该被拷——这决定了你是否需要、以及如何实现 __deepcopy__()。


















