1 list的可變性
在python程式中,清單list的存貯方式與int的存貯方式是不同的,換句話說,python中可變類型的存儲方式與不可變類型的存儲方式是不同的。
例如,對于單一的元素,有如下操作時:
a = 1
b = a
b = 5
print(a, b)
print(id(a),id(b)) # 列印兩個變量的位址
輸出>> 1 5
>> 140731421349024 140731421348896
即對整型變量的指派相當于一個真正的複制過程,兩個變量有了兩個不同的記憶體位址,其實是因為第3句把第二句覆寫掉了。
而對清單元素進行指派時:
a = [[0,1,2],2,3]
b = a
b[0] = 100
print(a, b)
print(id(a),id(b)) # 列印兩個變量的位址
輸出>> [100, 2, 3] [100, 2, 3]
>> 2847777749608 2847777749608
也就是說清單的指派本質上是名稱多了一個,換句話說,就是清單的名字多了一個,清單還是原來那個清單。兩個變量具有相同的記憶體位址。
下面看一下.copy()方法:
a = [1,2,3]
b = a.copy()
b[0] = 100
print(a, b)
print(id(a),id(b)) # 列印兩個變量的位址
輸出:>> [1, 2, 3] [100, 2, 3]
>> 2744646268360 2744646268744
看起來問題得到了解決,兩個變量記憶體位址也不一樣了,然而.cpoy()都叫它淺拷貝,原因在于如下所示:
a = [[0,1,2],2,3]
b = a.copy()
b[0][1] = 100
print(a, b)
print(id(a),id(b)) # 列印兩個變量的位址
print(id(a[0]), id(b[0])) # 列印兩個變量中變量的位址
輸出:>> [[[0, 100, 2], 2, 3] [[0, 100, 2], 2, 3]
>> 2649294660232 2649295880648
>> 2649294660168 2649294660168
可以看到,.copy()方法隻是将第一層清單的元素完全拷貝了一份新的記憶體,再多一層的清單就沒有拷貝了,是以稱它為淺拷貝。
當清單隻有一維時,是可以使用淺拷貝的。
深拷貝:
import copy
a = [[0,1,2],2,3]
b = copy.deepcopy(a)
b[0][1] = 100
print(a, b)
print(id(a),id(b)) # 列印兩個變量的位址
print(id(a[0]), id(b[0])) # 列印兩個變量中變量的位址
輸出:>> [[0, 1, 2], 2, 3] [[0, 100, 2], 2, 3]
>> 2521760495496 2521760589768
>> 2521760495112 2521760547336
深拷貝完全實作了我們想要的拷貝指派。
2 擴充總結
類似于list有上述特點的還有集合set 、字典dict,即可變元素,因為隻有可變元素在指派後修改之後才可能出現上述問題,不可變元素要不整個元素修改,要不然不改變,不存在上述問題。
指派語句的本質是為變量多起一個名字,即多一個記憶體指針,【最大的用處在于變量有了新名字原來的名字就可以删除了】。
類似于指派語句出現的問題還可能出現在函數傳參過程中,把原清單當作函數參數傳入函數時,如果函數中清單發生修改,原清單也會跟着修改。