问题的历史:在Python中,复制对象(特别是集合 - 列表、字典)是至关重要的:简单的变量赋值创建新的引用,而不是复制。为了避免在共同使用结构时出现不必要的“副作用”,出现了特殊方法copy()和deepcopy()。
问题:在处理嵌套集合(列表的列表、字典内的字典)时,简单的copy()只复制容器本身,而不复制内部元素。这可能导致难以捕捉的错误:嵌套元素的更改反映在所有“副本”中。
解决方案:copy.copy()创建一个浅复制(shallow copy)——新的顶层容器,但嵌套对象保持不变。copy.deepcopy()递归地复制所有嵌套对象。
代码示例:
import copy lst = [[1, 2], [3, 4]] shallow = copy.copy(lst) deep = copy.deepcopy(lst) lst[0][0] = 10 print(shallow) # [[10, 2], [3, 4]] — 嵌套对象已更改! print(deep) # [[1, 2], [3, 4]] — deepcopy保持了初始状态
关键特性:
有时写lst2 = lst[:]来复制列表是否足够?
lst2 = lst[:]创建列表的浅复制,但嵌套对象(例如,列表内的列表)仍然是相同的。对于平面列表——这足够;对于嵌套结构——则不够。
示例:
lst = [[1], [2]] lst2 = lst[:] lst[0][0] = 99 print(lst2) # [[99], [2]] — 嵌套元素在两个“副本”中都发生了变化
.copy()方法对所有集合工作一样吗?
不。例如,dict.copy()作为浅复制工作,list.copy()仅在Python 3.3中推出,而set则有set.copy()。对于用户自定义对象,.copy()的支持取决于实现的方法。
在任何地方都可以用deepcopy吗?这个安全和高效吗?
不。deepcopy是一种昂贵的操作:它可能会造成性能问题,尤其是在大型结构上,同时在不可复制/闭包或非标准对象上会失败。仅在确实需要完全独立、递归复制时使用deepcopy。
在测试中通过dict.copy()进行了“字典的字典”复制。这导致对嵌套结构的修复突然影响到其他测试(数据在全局上变异)。
优点:
缺点:
引入了copy.deepcopy(),在文档中描述了每个对象的嵌套级别和结构特征,避免在可以“手动”部分复制的大型数据中使用deepcopy。
优点:
缺点: