修復錯誤 - 字典在迭代過程中改變了大小
當我們在迭代過程中刪除、修改或新增字典物件中的新條目時,會發生此執行時錯誤。迭代字典時會發生此錯誤,但無論使用何種程式語言,幾乎所有可迭代物件都會發生。
下面的程式碼片段說明了在遍歷字典並同時進行更改時如何發生此錯誤。
cars = {
"brand": "Tesla",
"model": "Model S Plaid",
"year": 2021
}
for x in cars.keys():
cars["color"] = "white"
print(x)
在上面的程式碼塊中,我們在迭代時向原始字典新增了一個新專案。這將返回一個執行時錯誤,讓我們知道字典大小在迭代過程中發生了變化,這意味著我們不能在同時迭代的同時修改字典。
示例程式碼:
Traceback (most recent call last):
File "<string>", line 8, in <module>
RuntimeError: dictionary changed size during iteration
在對物件執行任何迭代時,刪除、新增或修改都被視為一種更改,並且不能在迭代時執行。下面的程式碼示例表明,如果我們在迭代時修改字典,此錯誤也會持續存在。因此,如果我們在迭代時從字典中刪除現有專案,我們仍然會得到同樣的錯誤。
示例程式碼:
cars = {
"brand": "Tesla",
"model": "Model S Plaid",
"year": 2021
}
for x in cars.keys():
del cars["model"]
print(cars)
輸出:
Traceback (most recent call last):
File "<string>", line 8, in <module>
RuntimeError: dictionary changed size during iteration
在 Python 3 中,迭代一個不斷變化的物件被認為是一種糟糕的程式碼編寫風格並且不安全。通常,在程式設計中,我們不能在迭代物件的同時對其進行變異;此規則擴充套件到可迭代物件,例如列表甚至陣列。
然而,如果一個函式改變了一個物件,我們必須確保該函式只改變原始物件的副本,而原始物件保持不變。這是在同時迭代物件的同時對物件進行更改的廣泛使用的方法之一。
這是一種很好的做法,也是避免建立可能最終導致記憶體耗盡的無限迴圈例項的最佳方法。可以使用多種解決方案來處理此錯誤,我們將在此處逐一討論。
建立字典的淺拷貝
Python 為我們提供了 copy()
模組,它允許我們建立一個物件的副本,而不繫結到原始物件。這讓我們可以自由修改物件的副本,而保持原始物件完好無損。
請注意,在 Python 中使用賦值運算子無法實現相同的功能。使用賦值運算子不會建立原始物件的副本,而是建立引用原始物件的變數。
因此,對新物件所做的任何修改也會影響原始物件。新開發人員經常濫用此運算子。
示例程式碼:
import copy
cars = {
"brand": "Tesla",
"model": "Model S Plaid",
"year": 2021,
}
#creating a shallow copy
cars_copy = copy.copy(cars)
for x in cars_copy.keys():
cars["color"] = "black"
print(cars)
print(cars_copy)
輸出:
{'brand': 'Tesla', 'model': 'Model S Plaid', 'year': 2021, 'color': 'black'}
{'brand': 'Tesla', 'model': 'Model S Plaid', 'year': 2021}
在提供的示例程式碼中,我們使用複製模組的 copy
函式建立了一個字典副本,我們可以在不影響原始字典的情況下自由迭代。對字典副本進行更改允許我們遍歷字典而不會遇到錯誤。
或者,我們可以使用 **
運算子(通常稱為兩個星號運算子)來重寫上面的程式碼,如下所示。
示例程式碼:
cars = {
"brand": "Tesla",
"model": "Model S Plaid",
"year": 2021
}
#creating a shallow copy
cars_copy = {**cars}
for x in cars_copy.keys():
cars["color"] = "black"
print(cars)
**
運算子可以從一個字典中獲取鍵值對並將它們轉儲到另一個字典中。
儘管運算子在 Python 中被廣泛用於傳遞關鍵字引數,但我們在上面的程式碼中使用運算子來解包字典並獲取鍵值對。然後我們建立字典的副本並將解壓縮的值轉儲到這個新字典中。
輸出:
'brand': 'Tesla', 'model': 'Model S Plaid', 'year': 2021, 'color': 'black'}
從字典中刪除鍵值對在執行迭代時也不例外,因此應該遵循類似的方法。因此,使用相同的過程,我們將刪除名為 model
的鍵及其值 Model S Plaid
,如下所示。
示例程式碼:
import copy
cars = {
"brand": "Tesla",
"model": "Model S Plaid",
"year": 2021,
"color": "black"
}
cars_copy = copy.copy(cars)
for x in cars_copy.keys():
if x == "model":
del cars["model"]
print(cars)
輸出:
{'brand': 'Tesla', 'year': 2021, 'color': 'black'}
另一種解決方案是建立鍵的副本,然後我們可以在修改字典時對其進行迭代。但是,這僅適用於 Python 2 而不適用於 Python 3,因為在 Python 3 中完成時,鍵不會返回可迭代物件。
示例程式碼:
cars = {
"brand": "Tesla",
"model": "Model S Plaid",
"year": 2021,
"color": "black"
}
key_copys = list(cars.keys())
print(key_copys)
for key in list(key_copys):
if cars[key] == "model":
cars.pop("model")
print(cars)
示例輸出:
['brand', 'model', 'year', 'color']
{'brand': 'Tesla', 'model': 'Model S Plaid', 'year': 2021, 'color': 'black'}
將字典項轉換為列表
由於我們無法在進行更改時迭代字典,因此我們可以建立一個轉換列表並在對字典進行更改時迭代列表。迭代轉換列表而不是原始字典不會返回執行時錯誤。
示例程式碼:
cars = {
"brand": "Tesla",
"model": "Model S Plaid",
"year": 2021
}
for i in list(cars):
cars["color"] = "black"
print(cars)
輸出:
{'brand': 'Tesla', 'model': 'Model S Plaid', 'year': 2021, 'color': 'black'}
將鍵附加到空列表
為了避免在迭代時更改字典,我們可以在執行迭代時建立一個包含字典鍵的空列表。使用這個空列表,我們可以附加我們想要刪除或更改的所有鍵,然後使用 pop()
函式刪除鍵或使用 append
函式新增新的鍵值對。
這可以按照下面的程式碼所示執行。
示例程式碼:
cars = {
"brand": "Tesla",
"model": "Model S Plaid",
"year": 2021
}
list = []
for i in cars:
list.append(i)
for x in list:
if x == "model":
cars.pop(x)
print(cars)
輸出:
{'brand': 'Tesla', 'year': 2021}
如下所示,我們可以使用相同的過程將新的鍵值對新增到字典中,同時使用 for 迴圈進行迭代。
示例程式碼:
cars = {
"brand": "Tesla",
"model": "Model S Plaid",
"year": 2021
}
list = []
for i in cars:
list.append(i)
for x in list:
cars["color"] = "black"
print(cars)
輸出:
{'brand': 'Tesla', 'model': 'Model S Plaid', 'year': 2021, 'color': 'black'}
Isaac Tony is a professional software developer and technical writer fascinated by Tech and productivity. He helps large technical organizations communicate their message clearly through writing.
LinkedIn