淺復制和深復制的區別在於,淺復制只復制引用到新的列表中(引用可以理解為地址),不會創建新對象。而深復制創建新的對象,並把對象保存在新的地址中。淺復制和深復制對可變和不可變序列的影響是不一樣的。對可變序列的淺復制會帶來意想不到的結果。看示例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算法的公眾號

