Python的浅拷贝和深拷贝

来源:互联网 发布:表单令牌的作用 php 编辑:程序博客网 时间:2024/05/04 22:33
浅拷贝
对一个列表通过完整切片操作或者内建函数list()进行的拷贝属于浅拷贝,考虑如下代码:
L0 = [1, [2, 3]]L1 = L0[:]L2 = list(L0)r0 = [id(x) for x in (L0, L1, L2)]r1 = [id(x) for x in L0]r2 = [id(x) for x in L1]r3 = [id(x) for x in L2]
此时r0的3个元素是互不相同的,说明L0,L1, L2分别引用了不同的对象。
现在要注意了,可以看到r1,r2和r3是完全相同的三个列表,这说明L0,L1和L2中的元素分别引用了相同的对象。
接下来进行如下操作:
L2[0] = 4L2[1][1] = 4
此时L0和L1的值均变为了[1, [2, 4]]。可以看到L0和L1的第一个元素没有跟随L2发生变化,而第二个元素跟随L2发生了变化,这是因为数字是不可变的,所以在对L2[0]重新赋值时,重新创建了一个对象,而列表是可变的,所以对其进行了原地修改,这样对L2的修改自然会影响到同样引用了该对象的L1和L2。
另外copy模块提供的函数copy()也可以实现浅拷贝。

需要注意的是,列表的合并、重复操作也会产生浅拷贝的效果,如:

L0 = [1, [2, 3]]L1 = [4]L2 = L0 + L1
此时L0[1]和L2[1]引用的是同一个对象[2, 3]

L0 = [1, 2]L1 = [L0] * 2
此时L0和L1[0]、L1[1]引用的是同一个对象[1, 2]



深拷贝
为了避免浅拷贝带来的副作用,可以通过调用函数copy.deepcopy()进行深拷贝。考虑如下代码:
import copyL0 = [1, [2, 3]]L1 = copy.deepcopy(L0)r0 = [id(x) for x in L0]r1 = [id(x) for x in L1]
此时r0和r1不再相同,这里注意r0[0]和r1[0]还是相同的,但是由于数字是不可变的,所以这也是没关系的,不过r0[1]和r1[1]肯定是不同的。这样L0和L1就是两个完全无关的对象了。
需要注意的是,如果一个列表中的所有元素都是不可变的,则该列表只能进行浅拷贝,即使调用函数copy.deepcopy()也只能得到浅拷贝,因为在这种情况下根本没有必要使用深拷贝。所以说,通常只有当一个列表中含有列表类型的元素或者一个字典中的值序列中存在列表类型时,才会考虑使用深拷贝。


针对字典的浅拷贝方法与列表不同,可以采用内建函数dict()或者字典的内建方法copy()实现,代码如下:

D0 = {'a': [1, 2]}D1 = dict(D0)D2 = D0.copy()
原创粉丝点击