复制列表等多数内置可变集合,最简单的就是使用内置的构造方法。当然也可以使用 l2=l1[:] 来复制。

>>> l1 = [3, [55, 44], (7, 8, 9)]
>>> l2 = list(l1) 
>>> l2
[3, [55, 44], (7, 8, 9)]
>>> l2 == l1 
True
>>> l2 is l1 
False

然而,构造方法或 [:] 做的是浅复制(即复制了最外层容器,副本中的元素是源容器中元素的引用)。

l1 = [3, [66, 55, 44], (7, 8, 9)]
l2 = list(l1)          # 浅拷贝
l1.append(100)         # 不会影响到 l2
l1[1].remove(55)       # l1、l2 第一个元素都 remove 55
print('l1:', l1)       # [3, [66, 44], (7, 8, 9), 100]
print('l2:', l2)       # [3, [66, 44], (7, 8, 9)]
l2[1]+= [33, 22]       # 赋值操作,改变 l1、l2 第一个元素
l2[2]+= (10, 11)       # 赋值操作,改变 l2 第二个元素
print('l1:', l1)       # [3, [66, 44, 33, 22], (7, 8, 9), 100]
print('l2:', l2)       # [3, [66, 44, 33, 22], (7, 8, 9, 10, 11)]

使用 Python Tutor 网站可以查看动画效果。

这里,还需要关注的是 += 操作。

  • 对于可变对象来说, += 运算符是就地修改列表。
  • 对于元组来说, += 运算符是创建一个新元组,然后绑定。

如果想要进行深拷贝,使用 copy 模块。copy 模块提供了 deepcopycopy 函数能为任何对象提供深拷贝和浅拷贝。这两个函数分别使用了对象默认的 __copy__()__deepcopy__() 函数来进行拷贝。因此,可以更改对象对应函数来控制拷贝的具体行为。

deepcopy 能对存在自身引用的对象进行深拷贝的原因:内部维护一个字典,通过 id 判断对象。