Python 字典浅拷贝:为什么嵌套列表会一起变化?
对字典调用 copy() 后,只给“副本”的标签列表追加一个值,原字典却也变了。这不一定是复制失败,而是需要先区分外层字典和它引用的内层对象。
1. 用一个完整样例观察
下面的数据完全在程序里构造,不读取业务文件:
from copy import deepcopy
original = {"tags": ["python"], "enabled": True}
alias = original
print("赋值共享:", alias is original)
shallow = original.copy()
print("外层独立:", shallow is not original)
print("内层共享:", shallow["tags"] is original["tags"])
shallow["enabled"] = False
shallow["tags"].append("csv")
print("原始数据:", original)
deep = deepcopy(original)
deep["tags"].append("json")
print("深拷贝后的原列表:", original["tags"])
print("深拷贝后的新列表:", deep["tags"])
本样例得到:
赋值共享: True
外层独立: True
内层共享: True
原始数据: {'tags': ['python', 'csv'], 'enabled': True}
深拷贝后的原列表: ['python', 'csv']
深拷贝后的新列表: ['python', 'csv', 'json']
2. 看清发生变化的位置
alias = original 只是让两个名字指向同一个字典。
original.copy() 则创建了新的外层字典。因此,给 shallow[“enabled”] 重新赋值,不会替换 original 里的同名字段。
但浅拷贝中的 tags 仍然引用同一个列表。append 修改的是这个共享列表,所以原字典也看得到新增的 csv。判断“原数据为什么变了”时,应先区分重新绑定字段和修改字段引用的对象。
这也说明,只检查 shallow is not original 还不够。若业务要求嵌套数据独立,还需要检查要修改的内层对象。
3. deepcopy 解决的是本例的嵌套共享
本例用 deepcopy 创建了另一份嵌套列表,给 deep[“tags”] 追加 json 后,original[“tags”] 保持不变。
这不意味着任何场景都应该把 copy 换成 deepcopy。代码如果只替换外层字段,浅拷贝可能已经满足需要;如果某些对象本来就应共享,递归复制也可能偏离设计。Python 官方 copy 文档说明了两种复制方式及自定义对象的复制协议。
4. 明确验证范围
本次在本机 Python 3.9.6 中执行了八项断言:赋值引用相同、浅拷贝外层独立、浅拷贝内层共享、外层字段独立、共享列表变更、深拷贝列表独立、深拷贝后原列表保持不变、深拷贝新列表符合预期。
样例只覆盖字典、列表、字符串和布尔值,没有测试自定义对象、文件、网络连接或性能开销。遇到这些对象时,应另外确认其复制语义,不把教学样例当成完整的对象复制方案。
官方参考:docs.python.org/3/library/copy.html。
本文由 AI 辅助整理,示例使用独立构造的数据并已在本机执行自检。
- 点赞
- 收藏
- 关注作者
评论(0)