Pyton 的變數其實都是一個記憶體參考, 尤其容器物件可以裝載各種其他物件, 放進容器中的其實也是參考, 例如下面的串列 a, 我們複製了 3 個 a 當作元素來組成 b 串列 :
>>> a=[1, 2, 3]
>>> b=[a, a, a]
>>> b
[[1, 2, 3], [1, 2, 3], [1, 2, 3]]
用 is 運算子檢驗, b 地三個元素之記憶體位址與原始物件 a 的記憶體位址是相同的, 即 b[0], b[1], b[2] 都指向 a 的位址 :
>>> b[0] is a
True
>>> b[1] is a
True
>>> b[2] is a
True
用 id() 函式查詢記憶體位址可知確實都是相同位址 :
>>> id(a)
1885677182592
>>> id(b[0])
1885677182592
>>> id(b[1])
1885677182592
>>> id(b[2])
1885677182592
所以 b 裡面的元素都是指向原始物件 a 的參考而已, 並非複製了三份 a 作為元素, 因此它們彼此是連動的 (都指向同樣的記憶體位址), 如果修改其中任一個, 其他變數也會被改變, 例如 :
>>> a[0]=999
>>> b
[[999, 2, 3], [999, 2, 3], [999, 2, 3]]
>>> b[2][2]=7
>>> a
[999, 2, 7]
>>> b
[[999, 2, 7], [999, 2, 7], [999, 2, 7]]
如果要避免這種因為共同指向原始物件的連動關係, 則可用 copy.deepcopy() 函式, 它會執行物件的深度複製, 亦即複製內容到另一段記憶體內 :
>>> import copy
>>> dir(copy)
['Error', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_copy_dispatch', '_copy_immutable', '_deepcopy_atomic', '_deepcopy_dict', '_deepcopy_dispatch', '_deepcopy_list', '_deepcopy_method', '_deepcopy_tuple', '_keep_alive', '_reconstruct', 'copy', 'deepcopy', 'dispatch_table', 'error']
使用時可從 copy 複製 deepcopy() 函式 :
>>> from copy import deepcopy
>>> c=[deepcopy(a), deepcopy(a), deepcopy(a)] # 也可以用 3*[deepcopy(a), ]
>>> c
[[999, 2, 7], [999, 2, 7], [999, 2, 7]]
>>> id(c[0])
1885683609600
>>> id(c[1])
1885675886592
>>> id(c[2])
1885682836992
>>> id(a)
1885677182592
深度複製是將內容拷貝一份到另一段記憶體上儲存, 所以 c 串列的三個元素位址都不同, 也與 a 不同, 因此更改內容也只影響自己, 不會連動 :
>>> c[1][1]=888
>>> c
[[999, 2, 7], [999, 888, 7], [999, 2, 7]]
>>> a
[999, 2, 7]
沒有留言:
張貼留言