浅拷贝仅复制顶层,嵌套对象共享引用;深拷贝递归复制全部层级,彻底隔离引用;自定义类需实现__deepcopy__以安全处理不可序列化资源。

浅拷贝只复制顶层,嵌套对象仍共享引用
当你用 copy.copy() 或切片(如 list[:] )、dict.copy() 复制含列表、字典、自定义类实例等嵌套结构的对象时,新对象的顶层是独立的,但内部所有可变子对象(比如子列表、子字典)仍然指向原对象里的同一内存地址。这意味着修改子对象内容会同时影响新旧对象。
常见错误现象:
- 修改
new_list[0].append(99)后,original_list[0]也多了99 -
id(new_dict['items']) == id(original_dict['items'])返回True
适用场景:只读或确定不修改嵌套内容时,浅拷贝开销小、速度快。
深拷贝递归复制全部层级,彻底隔离引用
copy.deepcopy() 会递归遍历整个对象图,为每个可变子对象都创建新副本,确保新旧对象在内存中完全独立。这是解决嵌套引用污染的直接方式。
实操建议:
- 必须导入
import copy,不能直接用copy() - 对含循环引用的对象(如父子节点互相持有),
deepcopy能自动检测并正确处理,不会无限递归 - 性能代价明显:深度越深、对象越复杂,耗时和内存占用越高;对纯不可变对象(如
tuple中只含int/str),deepcopy实际会复用原对象(优化行为,无需担心)
示例:
import copy
original = [[1, 2], {'a': [3, 4]}]
shallow = copy.copy(original)
deep = copy.deepcopy(original)
<p>shallow[0].append(5) # original[0] 也被改了
deep[0].append(6) # original[0] 不受影响</p>
自定义类需重写 __deepcopy__ 才能控制深拷贝逻辑
如果类里有文件句柄、线程锁、数据库连接等无法序列化的资源,或你希望跳过某些字段、延迟加载某部分,直接调用 deepcopy 会失败或行为异常。
这时需要在类中定义 __deepcopy__ 方法,接收 memo 参数(用于缓存已拷贝对象,避免重复和循环问题):
class DatabaseConnection:
def __init__(self, url):
self.url = url
self._conn = None # 运行时资源,不应拷贝
<pre class="brush:python;toolbar:false;">def __deepcopy__(self, memo):
new_obj = DatabaseConnection(self.url) # 新建实例,不复制 _conn
memo[id(self)] = new_obj
return new_obj
否则会抛出 TypeError: cannot pickle '_thread.lock' object 类错误。
不是所有对象都能被 deepcopy 安全处理
deepcopy 依赖对象可被“pickle”,遇到以下情况会出问题:
- lambda 函数、嵌套函数、动态生成的类(未绑定到模块)——报
AttributeError或PicklingError - 带
__slots__但没定义__getstate__的类,可能漏拷某些属性 - 涉及 C 扩展对象(如 NumPy 数组)通常支持,但某些特殊结构(如 mmap 文件视图)可能不兼容
调试技巧:用 try/except 包裹 deepcopy,捕获 TypeError 或 AttributeError,再检查具体哪个字段触发失败。
真正麻烦的往往不是“要不要深拷”,而是“哪些字段不该深拷、哪些要手动重建”——尤其在数据管道或配置对象里混入运行时状态时,得一层层看清楚引用链。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











