在Python中,变量赋值(例如,a = b)并不会复制对象本身,而是创建一个新的名称(引用)指向现有对象。如果这是一个可变对象,通过一个名称的更改将在另一个名称中可见。
浅拷贝(shallow copy),例如通过copy.copy(obj)或者使用切片[:]创建新的顶层对象,但内部嵌套对象通过引用进行复制(即两个结构"看"向相同的子容器)。如果修改嵌套对象,则通过两个对象都能看到更改。
示例:
import copy lst1 = [[1,2], [3,4]] lst2 = copy.copy(lst1) # 或者 lst1[:] lst1[0][0] = 100 print(lst2) # [[100, 2], [3, 4]]
lst2是一个新列表,但它的第一个元素是相同的嵌套列表。
问题: lst2 = lst1[:]和lst2 = copy.copy(lst1)有什么区别?
答案: 在实践中,对于普通(单层)列表没有区别——这两种方法都创建了列表的浅拷贝。然而,对于用户定义的容器类,可能会有不同的行为(例如,如果实现了自定义的__copy__方法)。同样,对于其他类型(dict、set等)而言,使用专门的copy模块会更安全。
import copy lst1 = [1, 2, 3] lst2 = lst1[:] lst3 = copy.copy(lst1) print(lst2 == lst3) # True
故事
在一个处理配置的项目中,开发人员通过赋值params = default_params复制了默认参数,并期望"独立"地更改它们。最终,任何副本中的更改都会导致应用程序的所有部分出现级联更改,因为实际上是与同一个对象在一起工作。
故事
一个经验不足的程序员使用列表的浅拷贝存储游戏状态(game_states = states[:])。在嵌套结构(棋盘上的棋子列表)中,一个状态内部的更改会渗透到其他状态,从而破坏了回滚历史和步骤的重复。
故事
在一个面向对象的应用程序中,克隆数据时选择了切片和copy.copy()之间的选择。但是结构中遇到了自己的类,且有自己的复制方法,这仅在使用copy.copy()时考虑到了。切片忽略了对象复制的逻辑,导致了不明显的bug。