最近在Python列表操作中遇到了一些问题,挖出了深拷贝与浅拷贝两个概念,用一篇博客来记录一下,谈谈我自己的理解。
引入:
问题一:
L=[1,2,3,4]
L1=L
L1.remove(1) #从列表中移除第一个值为1的数
print(L) # L=[1,2,3,4] or [2,3,4]???
事实证明,得到的是L=[2,3,4],这是为什么呢?由于这是因为从始至终只有一个列表,我们只是创建了新的变量,即相当于为列表重新加了一个名字,就像是一个人有很多个外号一样,你用其中一个外号去让他坐下,他坐下了,再称呼他另外一个外号,他还是已经坐下了,所以用不同变量名对列表进行操作,最后都是得到改变后的列表。再来看下面这种情况:
L=[1,2,3,4]
L2=L[:]
L2.remove(1) #从列表中移除第一个值为1的数
print(L) # L=[1,2,3,4] or [2,3,4]???
这次,我们得到的又是什么结果呢?这次结果是L=[1,2,3,4]。原因在于我们通过列表的切片操作得到了一个新的列表,就好像是我们克隆了一个新的人出来,虽然和之前的列表一模一样,但依然是两个相互独立的个体,对其中一个进行操作,不会影响另外一个。
L=[1,2,3,4]
L3=L.copy()
L3.remove(1) #从列表中移除第一个值为1的数
print(L) # L=[1,2,3,4] or [2,3,4]???
同上,我们依然得到一个复制后的新的列表,所以结果依然是L=[1,2,3,4]
下面重点来看一下L.copy()函数
L=[1,2,3]
L1=[4,5,L]
L2=L1.copy()
L[0]=7 #从列表中移除第一个值为1的数
print(L2) # L=[1,2,[1,2,3]] or [1,2,[7,2,3]]???
结果是L2=[1,2,[7,2,3]],有的人可能会纳闷:我不是已经复制了吗,得到一个新的列表了,那为什么值还会改变呢?下面我们通过一张图来表示拷贝过程:
图
从这张图中我们可以看出,拷贝后L2和L1共同拥有4,5,和L,
改变L的某一个指向,那么L1,L2对应的元素L的指向也会改变。
这时,又会有另外一个容易出错的问题:
L=[1,2,3]
L1=[4,5,L]
L2=L1.copy()
L1[0]=6
print(L2)#L2=[4,5,[1,2,3]] or[6,5,[1,2,3]]???
通过上面那张图可能很多人又会觉得应该是L2=[7,5,[1,2,3]],这是错误的。其实仔细想想,答案很清晰,我们复制的是列表每个空的指向,现在L2复制了L1的指向,L1的指向变化了,这两者是两个独立的个体,所以L2的指向不会改变。与上面的相比较,L的变化是它自己指向的变化,不会引起L1,L2指向的变化,L1,L2,依旧指向的是4,5,L,变化的只是L指向的内容,所以L指向的变化,会引起L1,L2最终内容的变化。
在这个复制过程中,只复制了一层变量,不会复制深层变量绑定的对象,这样的拷贝过程叫作浅拷贝,与之对应的就是深拷贝,我们来举一个深拷贝的例子:
L=[1,2,3]
L1=[4,5,L]
L2=copy.deepcopy(L1)
L[0]=5
print(L2)#打印结果为[4,5,[1,2,3]]
print(L1)#打印结果为[4,5,[5,2,3]]
看到这里,大家应该很清楚了,深拷贝已经将所有的东西都复制了一份,也就是说4,5,L以及L指向的1,2,3都拷贝了,现在更改的是原来的L的指向,所以只有L1会变,L2不会发生改变。
浅拷贝呢,肯定要比深拷贝的运算速度快,我们平常接触到的也基本就是浅拷贝了,但是特定场景下,深拷贝会解决浅拷贝的一系列问题,比如前面介绍到的,值的改变等。
ps:欢迎批评指正。