关于python:如何避免”runtimeerror:dictionary changed size during iteration”错误?

How to avoid “RuntimeError: dictionary changed size during iteration” error?

我用同样的错误检查了所有其他问题,但没有找到有帮助的解决方案。=/

我有一本列表词典:

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

其中一些值为空。在创建这些列表的最后,我想在返回词典之前删除这些空列表。目前我正试图这样做:

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

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


在python 2.x中,调用keys生成一个密钥的副本,您可以在修改dict时迭代该密钥:

1
for i in d.keys():

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

另一种方法是使用list强制制作密钥的副本。这一个也适用于python 3.x:

1
for i in list(d):


只需使用字典理解将相关项复制到新的字典中即可。

1
2
3
4
5
>>> 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]}


您只需使用"复制"即可:

通过这种方式,您可以迭代原始字典字段,并且可以动态地更改所需的dict(d dict)。它适用于每个Python版本,所以更清楚。

1
2
3
4
5
6
7
8
9
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]}

我会尽量避免首先插入空列表,但通常会使用:

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

如果在2.7之前:

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

或者只是:

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


对于Python 3:

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


在这种情况下,我喜欢做一个深度复制,并在修改原始dict的同时循环该复制。

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


运行时错误的原因是,当数据结构在迭代过程中发生更改时,无法对其进行迭代。

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

1
2
3
4
5
6
7
8
9
10
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)