HOWTO · Python

如何在 Python 中複製一個字典

說明指派如何讓兩個名稱指向同一個字典、何時淺層複製已足夠,以及巢狀值需要獨立時如何使用深層複製。

本頁內容

若要建立新的外層字典,請使用 dict(source) 或 source.copy()。兩者都是淺層複製,巢狀值仍會共用。若巢狀的可變值也必須彼此獨立,請使用 copy.deepcopy(source)。使用 = 指派不會建立複本。如需背景資料,請參閱 Python 字典教學和 Python 的 copy 模組文件。

指派字典會建立別名

指派會讓另一個名稱綁定至同一個字典物件。透過任一名稱修改字典,都會反映在兩者上。重新綁定名稱則不同:它會讓該名稱指向別處,不會改變原物件或另一個名稱所指向的物件。

以下完整範例先透過 meal 修改共用的字典,再將 meal 重新綁定至另一個新字典:

food = {"Fruit": "Orange", "Vegetable": "Lettuce"}
meal = food
meal["Fruit"] = "Banana"
print(food)
print(meal)
meal = {"Fruit": "Apple"}
print(food)
print(meal)
{'Fruit': 'Banana', 'Vegetable': 'Lettuce'}
{'Fruit': 'Banana', 'Vegetable': 'Lettuce'}
{'Fruit': 'Banana', 'Vegetable': 'Lettuce'}
{'Fruit': 'Apple'}

前兩個輸出值相同,因為在那個時點,兩個名稱都指向同一個物件。將新字典指派給 meal 之後,只有 meal 指向新建立的字典;food 仍指向原本的字典。這有助於理解,為什麼透過一個名稱變更值會從另一個名稱看見,而替換變數所指向的物件,並不會替換另一個名稱參照的物件。範例中的輸出標籤維持英文,因為程式碼與輸出區塊會原樣保留。

兩個 Python 字典變數指向同一個字典

淺層複製字典

內建的 dict() 建構子和字典的 copy() 方法都會建立新的外層字典。在複本中更新或移除頂層項目,不會改動原字典。不過,兩個字典仍指向相同的巢狀清單、字典或其他可變值,因此修改這些值時,變更可能會透過兩個名稱看見。

在此,by_constructor 和 by_method 各自取得一個獨立的外層字典。巢狀清單仍由兩者共用;將新值指派給 name 鍵,則只會影響所選的複本:

source = {"name": "Sherlock", "numbers": [1, 2, 3]}
by_constructor = dict(source)
by_method = source.copy()
by_constructor["name"] = "Holmes"
by_constructor["numbers"][1] = 4
print(source)
print(by_constructor)
print(by_method)
{'name': 'Sherlock', 'numbers': [1, 4, 3]}
{'name': 'Holmes', 'numbers': [1, 4, 3]}
{'name': 'Sherlock', 'numbers': [1, 4, 3]}

當你需要獨立的外層對應表,且可以接受巢狀值共用時,選用任一種淺層複製方式即可。淺層複製不會遞迴複製巢狀物件。例如,將不同的值指派給 by_constructor["name"] 不會改變來源字典的 name,因為這個鍵分別屬於兩個獨立的外層字典。相較之下,變更 numbers 中的項目會反映在三個輸出字典中,因為清單本身是共用的。若值是另一個字典,原理也相同:頂層複本仍保留指向該內層字典的參照。如果只需隔離其中一個巢狀值,可以單獨複製該值;若要讓整個巢狀結構彼此獨立,則使用深層複製。

Python 淺層複製會建立新的外層字典

深層複製巢狀字典

如果巢狀的可變值也需要各自獨立的複本,請使用 copy.deepcopy()。它會遞迴複製所包含的物件,因此下方範例可以修改清單項目和內層字典的值,而不影響原字典。當你打算獨立編輯巢狀資料時,這種方式很有用;若本來就要共用巢狀值,則不必使用。

import copy

source = {
    "numbers": [1, 2, 3],
    "address": {"city": "Miami"},
}
independent = copy.deepcopy(source)
independent["numbers"][1] = 4
independent["address"]["city"] = "Lexington"
print(source)
print(independent)
{'numbers': [1, 2, 3], 'address': {'city': 'Miami'}}
{'numbers': [1, 4, 3], 'address': {'city': 'Lexington'}}

如果後續修改不應在來源結構與工作複本之間互相影響,深層複製就很有用,例如建立一份巢狀設定的版本並獨立修改。它會複製巢狀容器,而不只是外層字典,因此上方範例中的清單和內層 address 對應表都維持不變。大型結構的深層複製可能比淺層複製耗費更多時間與記憶體,因此應依照是否需要獨立的巢狀物件來選擇。Python 物件可以自訂複製行為,有些資源也無法有意義地複製;處理這類物件時,請參閱 copy 模組文件。

實際上,選擇取決於哪些物件需要共用:指派會共用整個字典,淺層複製只會分開外層字典,深層複製則會連巢狀物件也分開。copy 模組文件說明了淺層與深層複製如何處理複合物件,以及具有自訂複製行為的物件。