用 list.copy() 備份串列之後,可以繼續在副本上處理資料。當串列裡還有串列,備份後就有兩個地方可以修改:外面的串列,以及裡面的串列。
先不換掉裡面的串列,只在末端加入資料:
rows = [[1], [2]]
backup = rows.copy()
print(backup is rows) # False
print(backup[0] is rows[0]) # True
backup[0].append(9)
print(rows) # [[1, 9], [2]]
is 比較的是物件身分(可參考 Python 的 is 和 == 差在哪)。外面的兩個串列不同,但索引 0 取得的仍是同一個內部串列。append(9) 修改了裡面的內容,從 rows 取得它時,數字 9 也在裡面。
Python 的 copy 文件把這種方式稱為淺拷貝(shallow copy):建立新的容器,再放入對原有元素的參照。複製後的外部串列可以各自增減元素,原有元素本身則繼續共用。
接著在同一次執行中,把 backup[0] 換掉:
backup[0] = [7]
print(backup) # [[7], [2]]
print(rows) # [[1, 9], [2]]
指定 backup[0] = [7],會把 backup 索引 0 的元素換成新的串列。rows 並沒有做這個替換,所以保留剛剛被加入 9 的串列。兩種操作都寫了 backup[0],但 append 會修改取得的串列,指定新值則會替換外部串列的元素。
需要獨立的內部資料時
如果備份後還要修改內部串列,而且希望原資料維持原樣,可以在修改前使用 deepcopy()。以下重新建立資料:
from copy import deepcopy
rows = [[1], [2]]
backup = deepcopy(rows)
backup[0].append(9)
print(rows) # [[1], [2]]
print(backup) # [[1, 9], [2]]
深拷貝會遞迴複製內部物件,對這組純串列資料來說,修改 backup[0] 已經不會影響 rows[0]。不過這個做法也會複製原本可能想共用的資料,並不需要為了備份就一律使用。
像是先複製清單,再從副本刪掉不需要的項目,沒有修改保留下來的內部資料,淺拷貝就能讓兩份清單各自增減。是否需要深拷貝,可以從後續實際會修改哪些資料來決定。

