图解浅复制和深复制的区别

浅复制和深复制的区别在于,浅复制只复制引用到新的列表中(引用可以理解为地址),不会创建新对象。而深复制创建新的对象,并把对象保存在新的地址中。浅复制和深复制对可变和不可变序列的影响是不一样的。对可变序列的浅复制会带来意想不到的结果。看示例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。
图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。
图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
  1. l2修改对l2没影响
  2. l2修改对l2有影响
  3. l2修改对l1有影响
  4. 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算法的公众号
在这里插入图片描述

posted @ 2020-04-29 21:10  WeifaGan  阅读(3784)  评论(0编辑  收藏  举报