图解浅复制和深复制的区别
浅复制和深复制的区别在于,浅复制只复制引用到新的列表中(引用可以理解为地址),不会创建新对象。而深复制创建新的对象,并把对象保存在新的地址中。浅复制和深复制对可变和不可变序列的影响是不一样的。对可变序列的浅复制会带来意想不到的结果。看示例1
示例1
>>>a = [[1],2,3,4]
>>>b = list(a)
>>>a[0].append(0)
>>>a
[[1, 0], 2, 3, 4]
>>>b
[[1, 0], 2, 3, 4]
对于复制列表,最简单的方式是使用内置类型的构造方法list(),也能使用[:]复制副本。不管是构造方法还是[:]都是浅复制。从示例1可以看到,对a的第0个元素进行了修改,但是b也发生了改变。为什么呢?我先要理解a和b之间发生了什么?见下面图1。
因为浅复制只是复制了引用到新的列表中,他们的引用还是一样的,a和b的第0个元素都是指向列表[1],因为列表是可变序列,可以原地修改,所以修改后引用不变。这就导致了a的修改会带来了b的改变。再看看示例2
示例2
>>>a = [1,2,3,4]
>>>b = list(a)
>>>a[0]=0
>>>a
[0, 2, 3, 4]
>>>b
[1, 2, 3, 4]
示例2中a的修改并没有带有b的变化,他们之间有发生了什么?看下面图2。
因为浅复制的原因,本来a,b的引用都是一样的,然后对a的第0元素进行了修改,由于该元素是不可变序列,要改变只能重新创建新的对象,所以a的第0个元素的引用发生了改变,但这并不影响到b的第0个元素的引用。好了,再看一个复杂点的例子,看示例3
示例3
>>>l1=[1,[2,3,4],(5,6,7)]
>>>l2=list(l1)
>>>l1.append(10)#1
>>>l1[1].remove(3)#2
>>>l2[1] +=[8,9]#3
>>>l2[2] += (10,11)#4
- l2修改对l2没影响
- l2修改对l2有影响
- l2修改对l1有影响
- l2修改对l1没影响
浅复制的结果可能不是你想要的,那么如何做深复制呢?。事实上,copy模块提供的copy和deepcopy函数能为任意对象做浅复制和深复制,看示例4。
示例4
>>>import copy
>>>l1=[1,[2,3,4],(5,6,7)]
>>>l2=copy.deepcopy(l1)
>>>l3=copy.copy(l1)
>>>l1[1].remove(3)
>>>l1
>>>l1=[1,[2,4],(5,6,7)]
>>>l2
>>>l1=[1,[2,3,4],(5,6,7)]
>>>l3
[1,[2,4],(5,6,7)]
l2是l1的深复制,l3是l2的浅复制,对l1第1个元素进行了修改,虽然该元素是可变对象,影响了l3,但是没有影响到l2。因为l2是深复制的原因,所以是创建了新的对象,有了新的引用,可以说是跟l2没有一毛钱关系了,他们只是内容一样而已。最后看一个有趣的现象,看示例5。
示例5
>>>l1=[1,[2,3,4],(5,6,7)]
>>>l1.append(l1)
>>>l1
[1, [2, 4], (5, 6, 7), [...]]
看l2最后的元素居然是神奇的[...],这究竟是什么回事?看看下面图3的引用就知道了,来!
可以看到,列表最后元素的引用指向了自身,自身的最有元素还是指向自身,这是一个无限循环引用,所以出现了[...]
以上,欢迎指出错误。
更多精彩文章请关注我:CVpython,一个专注于Python教程和CV算法的公众号