如何避免"RuntimeError:字典在迭代期间改变大小"错误?

use*_*318 203 python dictionary loops list

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

我有一个列表字典:

d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}
Run Code Online (Sandbox Code Playgroud)

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

for i in d:
    if not d[i]:
        d.pop(i)
Run Code Online (Sandbox Code Playgroud)

但是,这给了我运行时错误.我知道你在迭代它时不能在字典中添加/删除元素......这会是什么方法呢?

Mar*_*ers 370

在Python 2.x中,调用keys会生成一个键的副本,您可以在修改时重复该键dict:

for i in d.keys():
Run Code Online (Sandbox Code Playgroud)

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

另一种方法是使用list强制键的副本.这个也适用于Python 3.x:

for i in list(d):
Run Code Online (Sandbox Code Playgroud)

  • 为了澄清python 3.x的行为,d.keys()返回一个_iterable_(不是迭代器),这意味着它是一个直接在字典键上的视图.在d.keys()中使用`for i'确实可以在python 3.x _in general_中工作,但因为它迭代了字典键的可迭代视图,所以在循环中调用`d.pop()`会导致你发现的错误相同.列表中的for i(d)`模拟在迭代之前将键复制到列表中的稍微低效的python 2行为,对于像你这样的特殊情况. (6认同)
  • 用元组代替列表,因为它更快。 (4认同)
  • 在 python3.x 中,“list(d.keys())”创建与“list(d)”相同的输出,在“dict”上调用“list”返回键。“keys”调用(虽然不是那么昂贵)是不必要的。 (4认同)

Mar*_*ina 35

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

>>> 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]}
Run Code Online (Sandbox Code Playgroud)

  • `d.iteritems()`给了我一个错误.我使用`d.items()`代替 - 使用python3 (10认同)
  • 这适用于OP问题中提出的问题.然而,在多线程代码中遇到此RuntimeError之后来到这里的任何人都要注意,CPython的GIL也可以在列表理解的中间发布,并且您必须以不同方式修复它. (4认同)

Alo*_*rar 32

您只需要使用"复制":

在那种情况下,你迭代原始的字典字段,并在飞行中可以改变所需的字典(d字典).它适用于每个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]}
Run Code Online (Sandbox Code Playgroud)

  • `对于键,值在 dic.copy().items() 中:` (9认同)
  • 从这里的所有答案来看,对我来说,这是最简单和Pythonic的。 (7认同)

sin*_*ium 19

这对我有用:

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'}  
Run Code Online (Sandbox Code Playgroud)

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

  • 迄今为止该线程列出的所有选项中最快、最简单的修复。谢谢@singrium (2认同)

K.A*_*K.A 17

避免“字典在迭代错误期间更改大小”。

例如:“当您尝试删除某个键时”,

只需将“list”与“.items()”一起使用即可。这是一个简单的例子:

my_dict = {
    'k1':1,
    'k2':2,
    'k3':3,
    'k4':4
 
    }
    
print(my_dict)

for key, val in list(my_dict.items()):
    if val == 2 or val == 4:
        my_dict.pop(key)

print(my_dict)
Run Code Online (Sandbox Code Playgroud)

输出:

my_dict = {
    'k1':1,
    'k2':2,
    'k3':3,
    'k4':4
 
    }
    
print(my_dict)

for key, val in list(my_dict.items()):
    if val == 2 or val == 4:
        my_dict.pop(key)

print(my_dict)
Run Code Online (Sandbox Code Playgroud)

这只是一个例子。根据您的情况/要求进行更改。


ucy*_*cyo 12

对于Python 3:

{k:v for k,v in d.items() if v}
Run Code Online (Sandbox Code Playgroud)


Jon*_*nts 11

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

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

如果在2.7之前:

d = dict( (k, v) for k,v in d.iteritems() if v )
Run Code Online (Sandbox Code Playgroud)

要不就:

empty_key_vals = list(k for k in k,v in d.iteritems() if v)
for k in empty_key_vals:
    del[k]
Run Code Online (Sandbox Code Playgroud)

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

小智 9

您不能在 for 循环期间更改字典时对其进行迭代。制作一个列表并迭代该列表,它对我有用。

    for key in list(d):
        if not d[key]: 
            d.pop(key)
Run Code Online (Sandbox Code Playgroud)