凡是遇到 「用指標實作深層資料結構」 的程式語言, 一定要知道淺層拷貝 (shallow copy) vs 深層拷貝 (deep copy) 兩者的差別。 網路上文章很多, 但圖畫得很清楚的卻很少, 連英文的也不是很滿意, 只好自己來舉一個例子、 畫一張圖。
以 python 為例, 以下指令:
x = [[[1,2],[3,4]], [[5,6],[7,8]]] y = x z = x[:]
會產生上圖。 可以看到: y = x
只複製了最上 (外) 層陣列的位址。
而 z = x[:]
則把最上 (外) 層陣列的每一個元素
(也就是第二層每一個陣列的位址) 都複製過來。
接下來請在 python interpreter 裡面試試這幾個指令:
x[0][0][0] = 37
x[0][0] = [41,43]
x[0] = [[47,53],[59,61]]
每下一個指令之後, 都要順便檢查一下 x, y, z 的內容各有什麼變化。 前兩句修改到很深的地方, 所以做完之後, y 跟 z 都隨著 x 一起改變。 第三句修改到最淺層的地方, 所以做完之後 z 跟 x 就部分分家了: z[0] 看到的是舊的值, 跟 x[0] 看到的新值不一樣。
作業:
- 請把第三句做完的結果畫出來。
- 請實驗驗證:
u = x.copy()
的效果跟z = x[:]
一樣。 - 請實驗驗證:
v = [*x]
的效果跟z = x[:]
一樣。
用一個比較驚悚的比喻來說, y = x
只是幫一個人取綽號/別名,
根本算不上拷貝; 而 u = x.copy()
、 v = [*x]
跟 z = x[:]
通通算是 淺層拷貝 (shallow copy),
是不完整的複製, 最終製造出 ettin (雙頭怪)。
那要怎樣才可以真正的 深層拷貝 (deep copy) 呢?
這麼下指令可以製造出完整、 完全獨立於原變數之外的複製品
(複製人/同卵雙胞胎):
import copy u = copy.deepcopy(x)
現在再搜尋 「shallow 與 deep copy」 讀其他文章是不是清楚多了呢?
注意: 以上解說適用於陣列或字典等等複雜的資料結構。
如果 x 的內容是一個純量, 例如數字或字串,
那麼 y=x
就已經是完整的複製,
後續 x 的變化並不會影響到 y。
平常不太寫程式設計教學文的; 寫這篇是為了解釋 btrfs 檔案系統。
沒有留言:
張貼留言
因為垃圾留言太多,現在改為審核後才發佈,請耐心等候一兩天。