最可靠的方式是用copy.deepcopy()解决“幽灵修改”。浅拷贝仅复制外层,嵌套可变对象仍共享内存;含嵌套结构、需修改子项或跨函数复用时必须用深拷贝;但其有性能开销、不支持不可序列化对象及循环引用。

直接用 copy.deepcopy() 替代浅拷贝,是解决“幽灵修改”最可靠的方式。只要对象里含有可变嵌套结构(比如列表里套字典、字典里有列表),浅拷贝就只复制外层,内层仍共用同一块内存——改一个,另一个跟着变,问题就藏在这里。
哪些操作属于危险的浅拷贝
这些写法看似在“复制”,其实只是创建了新容器,里面的可变子对象仍是引用:
-
字典调用
.copy():如new_dict = old_dict.copy() -
列表调用
.copy()或切片[:]:如new_list = old_list[:] -
用
copy.copy():它和上面两种本质一样,都是浅拷贝
什么时候必须上深拷贝
出现以下任意一种情况,就要无条件换成 copy.deepcopy():
- 原始数据含嵌套结构,比如
[{'a': [1, 2]}, {'b': {'c': 3}}] - 你打算修改副本里的某个子项(例如
backup[0]['tags'].append('new')) - 数据会跨函数传递、被缓存、或用于配置模板(像购物车默认模板那种复用场景)
深拷贝不是万能银弹,注意这几点
它安全,但有代价,别滥用:
- 性能开销大:递归遍历所有层级,对超大嵌套结构会明显拖慢
- 不支持某些对象:比如文件句柄、线程锁、数据库连接等无法序列化的类型,会报
TypeError - 循环引用会卡住:如果对象内部自己引用自己(如
a = []; a.append(a)),deepcopy 默认会检测并抛出异常
更轻量的替代思路(适合特定场景)
如果深拷贝太重,又确实要避免共享,可以考虑手动构造独立副本:
- 对简单二维列表,用列表推导式:
new_matrix = [row[:] for row in old_matrix] - 对固定结构字典,用字典推导或
{k: v[:] if isinstance(v, list) else v for k, v in old.items()} - 用
json.loads(json.dumps(obj))快速“伪深拷贝”(仅限纯数据,不含函数、自定义类、datetime 等)

















