Python中的淺複製(shallow copy)和深複製(deep copy)


近期雜事太多,並且在準備一個tutorial,博客一直沒更新,9月最後一天了,總得寫點吧

今天記一下以前碰到過,最近又碰到的問題:python的深複製和淺複製
神奇的python中,copy竟然還有兩種,一深一淺(emm),爲什麼python中有,而c/c++中沒有這一概念呢?

python值管理方式

這本質原因是python採用的是基於值的管理方式,
而C語言中,系統會爲每個變量分配內存空間,當改變變量的值時,改變的是內存空間中的值,變量的地址是不改變的。
基於值的管理方式是什麼意思呢? 來看一個例子:

a = [1,2]
b = a
print ('a: ', a, 'id(a): ', id(a))
print ('b: ', b, 'id(a): ', id(b))

輸出:
('a: ', [1, 2], 'id(a): ', 4496519824)
('b: ', [1, 2], 'id(a): ', 4496519824)

可以發現a和b的內存地址相同,也就是說,a和b是完全相同的,b只是a的一個引用(也就是一個別名)
例如,帝都=北京,帝都就指的是北京這座城市,是完全相同的物理存在。這就是python的基於值的管理。

當我們採用“=”對參數進行賦值的時候,其實就是一個引用,若對引用進行了修改,原始參數也將被改變,這是我們在開發過程中很容易忽略的,也會導致我們的數據被意外修改,導致奇怪的bug。
請看例子:

a = [1,2]
b = a
b.append(3)
print ('a: ', a, 'id(a): ', id(a))
print ('b: ', b, 'id(a): ', id(b))

輸出:
('a: ', [1, 2, 3], 'id(a): ', 4335018640)
('b: ', [1, 2, 3], 'id(a): ', 4335018640)

可以看到,對b進行了增加一個元素,同時地,a也被修改了。

通過以上兩個小例子可以瞭解python值的管理方式,以及知道"="賦值的本質。
那麼當我們想複製出一個完全獨立於原始變量的變量,應該怎麼做呢?
這就需要淺複製(shallow copy) 和 深複製(deep copy)。

深複製與淺複製的使用及區別

在模塊copy中有deepcopy方法用於深複製,copy方法用於淺複製。
深複製和淺複製到區別,在於複製的對象中是否有可變類型,aha?,可變類型是什麼?
簡單複習一下,python中

可變類型有:列表,字典
不可變類型有:數字,字符串,元組

爲了循序漸進,這裏分兩個例子講解

例子1:

a = [1, [2, 3]]
a_equal = a
a_shallowcopy = copy.copy(a)
a_deepcopy = copy.deepcopy(a)
a[0] = 4    # 改變a中的第一個元素,即改變a中的數字
print'a:               ', a,       'id(a):             ', id(a)
print'a_equal:         ', a_equal, 'id(a_equal):       ', id(a_equal)
print'a_shallowcopy:   ', a_shallowcopy, 'id(a_shallowcopy): ', id(a_shallowcopy)
print'a_deepcopy:      ', a_deepcopy, 'id(a_deepcopy):    ', id(a_deepcopy)

輸出:
a: [4, [2, 3]] id(a): 4578314360
a_equal: [4, [2, 3]] id(a_equal): 4578314360
a_shallowcopy: [1, [2, 3]] id(a_shallowcopy): 4562436248
a_deepcopy: [1, [2, 3]] id(a_deepcopy): 4562362096

可以發現:
=賦值: 對於修改,受到影響,地址相同
淺複製:對於修改,不受影響,地址不相同
深複製:對於修改,不受影響,地址不相同

這麼一看,深複製和淺複製完全一樣啊,哪裏有區別?
還記得上面說的可變類型嗎? 剛一看剛剛改變list a當中的第一個元素,是一個數字吧。
數字是不可變類型,所以我們在修改不可變類型時,深複製和淺複製沒有區別。
那麼當我們改變a當中第二個元素,即list呢,會怎麼樣,請看例子2:

a = [1, [2, 3]]
a_equal = a
a_shallowcopy = copy.copy(a)
a_deepcopy = copy.deepcopy(a)
a[0] = 4    # 改變a中的第一個元素,即改變a中的數字
a[1][1] = 5     # 改變a當中第二個元素中的元素,即 改變a當中的list
print'a:               ', a,       'id(a):             ', id(a)
print'a_equal:         ', a_equal, 'id(a_equal):       ', id(a_equal)
print'a_shallowcopy:   ', a_shallowcopy, 'id(a_shallowcopy): ', id(a_shallowcopy)
print'a_deepcopy:      ', a_deepcopy, 'id(a_deepcopy):    ', id(a_deepcopy)

輸出:
a: [4, [2, 5]] id(a): 4454672504
a_equal: [4, [2, 5]] id(a_equal): 4454672504
a_shallowcopy: [1, [2, 5]] id(a_shallowcopy): 4438794392
a_deepcopy: [1, [2, 3]] id(a_deepcopy): 4438720240

可以發現:
=賦值: 對於修改,受到影響,地址相同
淺複製:對於不變類型的修改,不受影響;對於可變類型的修改,受影響,地址不相同
深複製:對於不變類型的修改,不受影響;對於可變類型的修改,不受影響,地址不相同

爲了不必要的麻煩,我們想要一個完全獨立的變量,要一個與原始變量解耦的變量,那請使用深複製吧。

還可以從另外一個角度理解深複製和淺複製的區別,那麼就是
淺複製僅複製父對象,不會複製對象內部的子對象(如例子2當中的a的第二個元素[2,3])
深複製不僅複製父對象,而且還複製內部的子對象。

什麼是子對象呢? 那就是可變類型,所以還是回到了開頭提到的,深複製和淺複製的區別,就要看變量中是否含有可變對象,哈哈。

(轉載請註明出處:https://blog.csdn.net/u011995719/article/details/82911392 by TensorSense)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章