我有一段python代码,它需要迭代字典中的所有键,其中循环的主体可能会更改字典。
只是试图迭代字典给了我RuntimeError: dictionary changed size during iteration
.搜索错误消息将我引向另一个问题,建议的解决方案是在开始迭代之前复制键列表。
然而,在我的方案中,这只是部分答案,因为我不仅需要迭代原始键,还需要迭代在迭代时添加的键。我需要的代码仅在没有更多键添加到字典时才终止,并且它已处理字典中已有的任何键。
到目前为止,我已经想出了这个:
processed = set()
while True:
keys = set(dictionary) - processed
if not keys:
break
for k in keys:
processed.add(k)
# Do something with k
这种方法似乎过于复杂。有什么方法可以简化它,并且仍然让它处理添加到字典中的所有键,而无需多次处理每个键?
在其他一些语言中,while 循环的前四行可以简单地写成:
while keys = set(dictionary) - processed:
但是,这在python中不起作用。我发现了一个关于在 python 的 while 循环中使用赋值作为条件的问题。但是提出的答案似乎都不适用于我的场景。
考虑相反的方法。跟踪仍需要处理的密钥,而不是已经处理的密钥。
remaining = set(dictionary.items())
while remaining:
key, value = remaining.pop()
# process the item
if item_to_add:
dictionary[new_key] = new_value
remaining.add((new_key, new_value))
这避免了每次迭代昂贵的集合差分运算。
如果您真的不知道在处理过程中添加了哪些键,那么您问题中的代码是好的。一种稍微不同的编写方式是:
keys = set(dictionary)
processed = set()
while keys:
for k in keys:
# Do something with k
processed.update(keys)
keys = set(dictionary) - processed
这样更好吗?更 糟?我想由你决定。
也许你可以改用OrderedDict
。迭代时添加密钥没有问题
>>> from collections import OrderedDict
>>> d = OrderedDict([(1, 2), (3, 4)])
>>> for k in d:
... if k == 1:d[5] = 6
... print(k)
...
1
3
5
您是更新密钥还是只是添加密钥?如果更新,则需要指定是否应再次处理密钥
我将代码重写为:
processed = set()
while set(dictionary) - processed:
for k in set(dictionary) - processed:
processed.add(k)
# Do something with k
这确实会复制set(dictionary) - processed
表达式,但仍使代码更易于阅读。