2020年9月14日 星期一

Shallow Copy vs Deep Copy

Python 的 Shallow Copy 凡是遇到 「用指標實作深層資料結構」 的程式語言, 一定要知道淺層拷貝 (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 裡面試試這幾個指令:

  1. x[0][0][0] = 37
  2. x[0][0] = [41,43]
  3. x[0] = [[47,53],[59,61]]

每下一個指令之後, 都要順便檢查一下 x, y, z 的內容各有什麼變化。 前兩句修改到很深的地方, 所以做完之後, y 跟 z 都隨著 x 一起改變。 第三句修改到最淺層的地方, 所以做完之後 z 跟 x 就部分分家了: z[0] 看到的是舊的值, 跟 x[0] 看到的新值不一樣。

作業:

  1. 請把第三句做完的結果畫出來。
  2. 請實驗驗證: u = x.copy() 的效果跟 z = x[:] 一樣。
  3. 請實驗驗證: 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 檔案系統

沒有留言:

張貼留言

因為垃圾留言太多,現在改為審核後才發佈,請耐心等候一兩天。