登入
首頁 所有文章 Python Python的淺拷貝與深拷貝
長也   2018-11-07 19:44:48(1年前)    576點閱   0喜歡  0收藏
Python的淺拷貝與深拷貝  

對象的賦值

請看一個範例:

a = 50
b = a
print(id(a))
print(id(b))
print(a is b)

這串代碼的執行結果如下

1557609488
1557609488
True

這個範例很簡單,將a的值50傳給b,但在python當中是將a的記憶體位址傳給b,也就是說b與a是共用同一個記憶體位址。

不過,我們如果將b的值改變為100

b = 100
print(id(a))
print(id(b))
print(a is b)

此時的輸出結果為

1557609488
1557610288
False

b就變成了一個新的變數,而並非與a指向同一個記憶體位址。

但是當我們把b的值又改回與a相同的50的時候

b = 50
print(id(a))
print(id(b))
print(a is b)

此時輸出為

1557609488
1557609488
True

會發現當值相同的時候,python會自動地將數值相同的變數指向同一個記憶體位址,不會再使用新的記憶體位址。

所以,我們可以理解為在python當中對象的賦值"="通常是傳遞位址而非傳值

a = [1, 2, 3]
b = a
a = [4, 5, 6]
print(a)
print(b)

而這串代碼的輸出為

[4, 5, 6]
[1, 2, 3]

a 與 b 並沒有一起改變,這樣代表 a 已經被重新指向一個新的記憶體位址,並不是原本的一開始宣告的a,而 b 仍然指向一開始宣告的 a ,因此 b 並不會隨著新的 a 改變。

a = [1, 2, 3]
b = a
a[0], a[1], a[2] = 4, 5, 6
print(a)
print(b)

如果直接修改裡面的值呢?這樣輸出結果為

[4, 5, 6]
[4, 5, 6]

因為這樣的操作並沒有改變a所指向的的記憶體位址,只是個別修改a所指向的記憶體中的值,由於b與a指向同一個記憶體位址,因此a與b的值會一起改變。

淺拷貝

淺拷貝主要是複製位址,並指向相同的位址,而不會使用新的記憶體位址

l = ['我', '好', '帥']
l2 = l
print(id(l))
print(id(l2))
print(l is l2)
l2[2] = '醜'
print(l[2])
print(l2[2])

這串代碼的輸出為

76367712
76367712
True
醜
醜

會發現將 l 賦予 l2 的時候,共用了記憶體位址,而當修改l2[2]的時候,l[2]也會一起改變,意味著 l 與 l2 共用了同一個記憶體位址,這屬於一種淺拷貝。

import copy
l = ['我', '好', ['帥', '哦']]
l3 = copy.copy(l)
l[2][0] = '醜'
print(l[2][0])
print(l3[2][0])

而上面這串使用copy.copy()方法的代碼之輸出為

醜
醜

可以發現當變更了l[2][0]的時候,l3[2][0]也同樣被變更了,由此可以得知 l[2] 與 l3[2] 是共用同一個位址的,所以屬於一種淺拷貝。

import copy
l = ['我', '好', ['帥', '哦']]
l5 = l[:]
l[2][0] = "醜"
print(l[2][0])
print(l5[2][0])

這串代碼進行了切片,而輸出為

醜
醜

也意味著進行切片操作也屬於一種淺拷貝,因為 l5[2][0] 隨著 l[2][0] 一起改變了。所以與copy.copy()方法的效果相同。

深拷貝

深拷貝會複製整個物件,並產生為一個新的物件,兩者不會參考同個記憶體位址

import copy
l = ['我', '好', ['帥', '哦']]
l4 = copy.deepcopy(l)
l[2][0] = "笨"
print(l[2][0])
print(l4[2][0])

這個代碼的輸出為

笨
帥

發現了 l4[2][0]  並沒有隨著 l[2][0] 改變而改變,這就屬於一種深拷貝

簡單的對象,淺拷貝與深拷貝無差異

import copy
a = 20
b = copy.copy(a)
c = copy.deepcopy(a)
a = 87
print(b)
print(c)

這串代碼輸出為

20
20

會發現這種情況下,無論使用淺拷貝或深拷貝都沒有差異,他們被拷貝出來的值都不會因為原始的值改變而一起改變。也就是說,在相對簡單的對象當中,淺拷貝與深拷貝是沒有差異的。

結論

  1. 在簡單的對象當中,淺拷貝與深拷貝是沒有差異的
  2. python當中的賦值都屬於傳遞參考
  3. 切片、copy.copy()、直接賦值都屬於淺拷貝
  4. copy.deepcopy()可以進行深拷貝

參考資料

图解Python深拷贝和浅拷贝

Python中副本,deepcopy的區別及原因

 

本文作者:長也

糾結與想不開的資管系學生,之前常碰PHP,現在常碰到的是Python,閒暇之餘就記錄一些筆記。

             

如要發表回覆,請先 登入

  0則回覆