# 深層拷貝與淺層拷貝的差異 [python官方文檔](https://docs.python.org/zh-tw/3/library/copy.html) 在python當中,如果想要複製一個list當中的元素時,我們可能會使用=(賦值) 但是這樣其實不能產生一個獨立的list,如果不太能理解的話,我們直接看程式碼 ```python= a = [1,2,3,4] b = a # b 為 a 的分身 print(f"b: {b}") # 先輸出b看看 b.append(5) # 將5加入b # 比較a跟b print(f"a: {a}") # 比較a跟b print(f"b: {b}") ``` 如果將上述的程式碼拿去輸出,就會發現修改b時,a也會同時被修改 那是因為b其實是類似於a的分身,因此它具有以下三個特性 * 和a完全相同(只有變數名字不同) * 記憶體位置相同 * b被修改,a也會被修改 這是屬於**直接複製**的方式,接下來我會介紹淺拷貝(淺複製) ## Shallow copy(淺拷貝) 淺拷貝是複製容器中元素的地址(記憶體位置),一般來說 如果只要處理一維陣列,那麼使用淺拷貝或是深拷貝都沒關係 用程式碼展示會比較清楚 : ```python= a = [1,2,3,4] b = a[:] # b 為 a 的分身 print(f"b: {b}") # 先輸出b看看 b.append(5) # 將5加入b # 比較a跟b print(f"a: {a}") # 比較a跟b print(f"b: {b}") print("------------------") a = [1,2,3,4] b = a.copy() # b 為 a 的分身 print(f"b: {b}") # 先輸出b看看 b.append(5) # 將5加入b # 比較a跟b print(f"a: {a}") # 比較a跟b print(f"b: {b}") print("------------------") a = [1,2,3,4] b = list(a) # b 為 a 的分身 print(f"b: {b}") # 先輸出b看看 b.append(5) # 將5加入b # 比較a跟b print(f"a: {a}") # 比較a跟b print(f"b: {b}") print("------------------") a = [1,2,3,4] b = [i for i in a] # b 為 a 的分身 print(f"b: {b}") # 先輸出b看看 b.append(5) # 將5加入b # 比較a跟b print(f"a: {a}") # 比較a跟b print(f"b: {b}") ``` 把上述的程式碼輸出就會發現,這四者都是淺拷貝,在一維陣列時不影響原本的陣列 但如果是二維陣列呢?因為淺拷貝是複製內容元素地址,假如內容元素是陣列 就會像直接複製一維陣列一樣 用程式碼會比較清楚 : ```python= a = [[1,2], [3,4]] b = list(a) b[0][0] = 0 print(f"a: ", a) print(f"b: ", b) ``` 上述的程式碼輸出會發現,a的元素也會被連帶修改,如果要避免這種狀況,就要使用深拷貝 ## Deep copy(深拷貝) 一般來說,深拷貝是用來製作一個完全獨立於複製對象的功能 它可以只複製對象的元素內容而不複製記憶體位置 所以可以用來複製多維陣列,在深度學習的領域很常用到 我們這邊介紹兩個方式去實現深拷貝 : ```python= from copy import deepcopy a = [[1,2], [3,4]] b = deepcopy(a) # 深拷貝 b[0][0] = 0 print(f"a: ", a) print(f"b: ", b) print("--------------------") a = [[1,2], [3,4]] # 任何一種淺拷貝+列表推導式 b = [[j for j in i] for i in a] b[0][0] = 0 print(f"a: ", a) print(f"b: ", b) ``` 上面兩個方式都可以達到深拷貝,但是第二種的淺拷貝只有在 n 維陣列的第n層可以放 所以我們在APCS常用的還是第一種,在歷屆考題也有用到的時候 淺拷貝也是,找一個順手的用就好 **如果怕麻煩就直接用copy跟deepcopy就好,兩個很相近也比較容易記** ## 導入function的List ```python= def azero_bone(a,b) : # 修改導入的List a[0] = -1 b[1] = -2 l1 = [1,2,3,4] l2 = [1,2,3,4] azero_bone(l1,l2) # global的List 也被修改 print(f"l1: {l1}") # l1: [-1, 2, 3, 4] print(f"l2: {l2}") # l2: [1, -2, 3, 4] ``` 所以要修改global List,可以**用global或是直接導入Function** 但是使用"導入Function"的方式還有一個好處 **修改其他local List** 程式碼如下 : ```python= def azero_bone(a,b) : # 修改導入的List a[0] = -1 b[1] = -2 def main() : l1 = [1,2,3,4] l2 = [1,2,3,4] azero_bone(l1,l2) # main的List 也被修改 print(f"l1: {l1}") # l1: [-1, 2, 3, 4] print(f"l2: {l2}") # l2: [1, -2, 3, 4] main() ``` ## 總結 * 淺/深拷貝在第一層均為不同記憶體位置 * 淺拷貝在第二層仍與原始變數為相同記憶體位置 * 深拷貝在第二層變數已為不同記憶體位置 * 深拷貝是一個完全獨立的變數