問題解析:Python中的變數複製備份,為什麼沒有達到效果?

博為峰網校發表於2022-11-09

背景

在運用Python進行開發程式碼過程中,會遇到變數複製備份的場景,但並沒有得到預期的結果,例如下面的例子:   加我VX:atstudy-js 回覆“測試”,進入 自動化測試學習交流群~~

lista = ['a', 'b', [1, 2, 3]]

listb = lista.copy()

lista[2].append(4)

print(lista) # ['a', 'b', [1, 2, 3, 4]]

print(listb) # ['a', 'b', [1, 2, 3, 4]]

程式碼本意是將lista複製給listb做個備份,再修改liasta,但是修改後發現listb也一併被修改了,沒有達到備份的效果,這個是什麼原因呢?

儲存方式

首先了解一下Python的變數在記憶體中的儲存方式。在基本資料型別中(包括set、list(tuple, str)、dict)都是採用引用的方式。

也就是說,每個變數都儲存的是這個變數的地址,而不是值本身,就算更復雜的巢狀結構,也是儲存是每個元素的地址而已,用一幅圖來表示。

如上圖所示,使用者看到的是 lista的4個元素值,但是記憶體中儲存的卻是4個元素地址。

當元素是列表時,第一層儲存的是列表的地址,第二層儲存的是列表元素的地址,第三層才是列表的值。當元素是字典的時候,與列表類似。

列表的增刪改

在明白了變數儲存方式後,繼續看下記憶體下的增刪改是怎麼變化的。

列表修改已有值

新增一個記憶體塊,再將引用的地址修改為新記憶體塊的地址。

列表新增一個值

新增一個記憶體塊,新增一個地址引用。

列表整體重新賦值

刪除變數地址和引用的值,新增地址和引用值的記憶體塊。

copy與deepcopy的區別

基於以上的理解,再來看兩種copy的區別就會更容易理解了,首先記住一個原則:

copy:不管多麼複雜的資料結構,淺複製都只會copy一層。

deepcopy:將整個變數記憶體全部複製一遍,新變數與原變數沒有任何關係。

舉個例子來驗證一下上面的結論:有如下的一段程式碼,最終的4個列表值是多少?

注意:引用deepcopy需要匯入copy庫。

import copy

a = [1, 2, 3, 4, ['a', 'b']]

b = a

c = copy.copy(a)

d = copy.deepcopy(a)

a.append(5)

a[1] = 20

a[4].append('c')

del a[0]

print(a)

print(b)

print(c)

print(d)

列表b

表示b也引用的a的地址,兩者引用的記憶體地址是一樣的。因此b和a的關係是緊密相連的,一模一樣。可以透過 id(a) 和id(b)比較,兩者是一樣的。

列表c

由於c是淺複製的a列表,因此只copy了第一層,也就是地址層。

所以,當a.append(5)時,新增了一個記憶體塊,但是c只有前5個記憶體塊,因此c沒有變化。

繼續a修改了a[1],然而這個值是屬於第一層,已經copy給了c,因此c也沒有變化。

繼續a修改了子列表,這個時候a複製給c的只是列表的地址,且a中的子列表地址和c中的子列表地址是指向同一個地方的,因此修改了a中子列表,c中的子列表也會相應的改變。

最後刪除a[0],與修改a[1]一致,與c無關。可以用圖再說明一下。

列表d

由於d是深複製的a列表,因此d是將a的地址和值一併複製過來,與a沒有半點關係,也就是說d和a是兩個完全獨立的記憶體塊,沒有任何交集。因此,後面a的任意修改都與d無關,用圖表示如下。

因此,程式執行出來後的結果就是:

a:[20,3,4,['a','b','c'],5]

b:[20,3,4,['a','b','c'],5]

c:[1,2,3,4,['a','b','c']]

d:[1,2,3,4,['a','b']]

總結

綜上,我們在使用copy的時候,一定要記住:copy只是複製了第一層,而deepcopy才是複製的全部資料。

因此就不難發現,文章背景中的程式碼使用備份功能時,備份列表需要使用deepcopy,而不是簡單的copy。

最後:

可以到我的個人V:atstudy-js,可以免費領取一份10G軟體測試工程師面試寶典文件資料。以及相對應的影片學習教程免費分享!其中包括了有基礎知識、Linux必備、Mysql資料庫、抓包工具、介面測試工具、測試進階-Python程式設計、Web自動化測試、APP自動化測試、介面自動化測試、測試高階持續整合、測試架構開發測試框架、效能測試等。

這些測試資料,對於做【軟體測試】的朋友來說應該是最全面最完整的備戰倉庫,這個倉庫也陪伴我走過了最艱難的路程,希望也能幫助到你!

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31407649/viewspace-2922620/,如需轉載,請註明出處,否則將追究法律責任。

相關文章