# 深層拷貝與淺層拷貝的差異
[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()
```
## 總結
* 淺/深拷貝在第一層均為不同記憶體位置
* 淺拷貝在第二層仍與原始變數為相同記憶體位置
* 深拷貝在第二層變數已為不同記憶體位置
* 深拷貝是一個完全獨立的變數