修复错误 - 字典在迭代过程中改变了大小
当我们在迭代过程中删除、修改或添加字典对象中的新条目时,会发生此运行时错误。迭代字典时会发生此错误,但无论使用何种编程语言,几乎所有可迭代对象都会发生。
下面的代码片段说明了在遍历字典并同时进行更改时如何发生此错误。
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