如何避免“ RuntimeError:字典在迭代过程中更改大小”错误?


257

我检查了所有其他问题,并发现了相同的错误,但没有找到有帮助的解决方案= /

我有一个列表字典:

d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}

其中某些值为空。在创建这些列表的最后,我想在返回字典之前删除这些空列表。当前,我正在尝试执行以下操作:

for i in d:
    if not d[i]:
        d.pop(i)

但是,这给了我运行时错误。我知道您无法在字典中进行迭代时添加/删除字典中的元素...那么解决这个问题的方法是什么?

Answers:


453

在Python 2.x中,调用keys会生成密钥的副本,您可以在修改时进行迭代dict

for i in d.keys():

请注意,这在Python 3.x中不起作用,因为它keys返回一个迭代器而不是列表。

另一种方法是用于list强制复制密钥。这也可以在Python 3.x中使用:

for i in list(d):

1
我相信你的意思是“调用keys使得副本钥匙,你可以遍历”又名plural右方向键?否则,如何迭代一个键?顺便说一句,我不是尼特采摘者,我真的很想知道这确实是一个或多个钥匙
HighOnMeat

6
用元组代替列表,因为它更快。
Brambor

8
为了阐明python 3.x的行为,d.keys()返回一个可迭代的(而不是迭代器),这意味着它是直接在字典键上的视图。一般而言,using for i in d.keys()确实可以在python 3.x 工作,但是由于它正在对字典键的可迭代视图进行迭代,因此d.pop()在循环期间进行调用会导致与您发现的错误相同的错误。for i in list(d)模拟在迭代之前将键复制到列表中的python 2稍微低效的行为,适用于您这样的特殊情况。
Michael Krebs '18

当您要删除内部字典中的对象时,您的python3解决方案不起作用。例如,您在dict A中有一个dic A和dictB。如果要在dict B中删除对象,则会发生错误
Ali-T

1
在python3.x中,list(d.keys())创建与相同的输出list(d),调用lista dict返回键。该keys呼叫(虽然不贵)是不必要的。
肖恩·布雷肯里奇

46

只需使用字典理解将相关项目复制到新字典中

>>> d
{'a': [1], 'c': [], 'b': [1, 2], 'd': []}
>>> d = { k : v for k,v in d.iteritems() if v}
>>> d
{'a': [1], 'b': [1, 2]}

为此,在Python 3

>>> d
{'a': [1], 'c': [], 'b': [1, 2], 'd': []}
>>> d = { k : v for k,v in d.items() if v}
>>> d
{'a': [1], 'b': [1, 2]}

11
d.iteritems()给我一个错误。我d.items()改用了-使用python3
wcyn

4
这适用于OP提出的问题。但是,在多线程代码中遇到此RuntimeError之后来到这里的任何人,请注意,CPython的GIL也可以在列表理解的中间发布,并且您必须进行不同的修复。
Yirkha '17

40

您只需要使用“复制”:

这样,您就可以遍历原始词典字段,并且可以随时更改所需的字典(d dict)。它适用于每个python版本,因此更加清晰。

In [1]: d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}

In [2]: for i in d.copy():
   ...:     if not d[i]:
   ...:         d.pop(i)
   ...:         

In [3]: d
Out[3]: {'a': [1], 'b': [1, 2]}

工作了!谢谢。
Guilherme Carvalho Lithg '19

13

我会尽量避免一开始就插入空列表,但是通常会使用:

d = {k: v for k,v in d.iteritems() if v} # re-bind to non-empty

如果2.7之前:

d = dict( (k, v) for k,v in d.iteritems() if v )

要不就:

empty_key_vals = list(k for k in k,v in d.iteritems() if v)
for k in empty_key_vals:
    del[k]

+1:最后一个选项很有趣,因为它仅复制那些需要删除的项目的键。如果仅需要删除相对于dict大小的少量项目,则可以提供更好的性能。
Mark Byers

@MarkByers是的-如果数量很多,则将dict重新绑定到经过过滤的新字典是一个更好的选择。总是对结构如何工作的期望
乔恩·克莱门茨

4
重新绑定的一个危险是,如果程序中某个地方有一个对象持有对旧dict的引用,它将看不到更改。如果您确定不是这种情况,那么请确保...这是一种合理的方法,但重要的是要了解它与修改原始字典并不完全相同。
Mark Byers

@MarkByers非常好-您和我(以及无数其他人)都知道,但这并不是所有人都知道的。而且我会把钱放在桌子上,它也没有在后面咬你:)
乔恩·克莱门茨

避免插入空条目的观点是非常好的。
Magnus Bodin

12

对于Python 3:

{k:v for k,v in d.items() if v}

简洁明了。也为我在Python 2.7中工作。
donrondadon


1

在这种情况下,我喜欢制作一个深层副本并在修改原始字典的同时循环遍历该副本。

如果查找字段在列表中,则可以枚举列表的for循环,然后将位置指定为索引以访问原始字典中的字段。


1

这为我工作:

dict = {1: 'a', 2: '', 3: 'b', 4: '', 5: '', 6: 'c'}
for key, value in list(dict.items()):
    if (value == ''):
        del dict[key]
print(dict)
# dict = {1: 'a', 3: 'b', 6: 'c'}  

将字典项目转换为列表会创建其项目列表,因此您可以对其进行迭代并避免使用RuntimeError


1

dictc = {“ stName”:“ asas”} keys = dictc.keys()用于键入列表(键):dictc [key.upper()] =“新值” print(str(dictc))


0

运行时错误的原因是,您无法在迭代期间更改数据结构时对其进行迭代。

实现所需内容的一种方法是,使用列表附加要删除的键,然后在字典中遍历列表时在字典上使用pop函数删除标识的键。

d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}
pop_list = []

for i in d:
        if not d[i]:
                pop_list.append(i)

for x in pop_list:
        d.pop(x)
print (d)

0

Python 3不允许在迭代(使用上面的循环)字典时删除。有多种选择可做。一种简单的方法是更改​​以下行

for i in x.keys():

for i in list(x)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.