如何避免出现“RuntimeError: dictionary changed size during iteration”错误?

501
假设我有一个列表的字典:
d = {'a': [1], 'b': [1, 2], 'c': [], 'd':[]}

现在我想要删除值为空列表的键值对。我尝试了以下代码:
for i in d:
    if not d[i]:
        d.pop(i)

但是这会导致一个错误:
RuntimeError: dictionary changed size during iteration

我知道在遍历字典时无法添加或删除条目。为了解决这个问题,我该如何绕过这个限制?

请参阅在迭代过程中修改Python字典,以了解可能导致问题的引用和原因。


除了下面给出的答案之外,您可能需要在从列表中删除元素的地方检查空列表。 您可以编写一个辅助函数:def remove_helper(d,k,elem):d [k] .remove(elem); 如果不是d [k]:del d [k] - joseville
从实际角度来看,可能有三种有意义的不同方法来做到这一点,并且在我的评估中,对这些方法的每一个有意义的微小变化最多可以通过考虑这里的6个答案来涵盖。令人有些担忧的是,还有8个未删除的答案,以及另外5个之前被删除的答案。 - Karl Knechtel
@joseville 为了明确起见:您建议在列表变为空时更新字典,而不是迭代删除空列表?这可能是一个实际的方法来解决上下文中更广泛的问题,具体取决于原始要求的确切情况。但我同意这并不足以证明需要单独回答。 - Karl Knechtel
15个回答

0
  • Python中的"RuntimeError: dictionary changed size during iteration"错误发生在我们在迭代字典时改变了它的大小。

  • 为了解决这个错误,使用copy()方法创建一个浅拷贝的字典进行迭代,例如my_dict.copy()

    my_dict = {'a': 1, 'b': 2, 'c': 3}
    
    for key in my_dict.copy():
        print(key)
        if key == 'b':
            del my_dict[key]
    
    print(my_dict) # ️ {'a': 1, 'c': 3}
    
  • 你也可以将字典的键转换成列表并迭代键的列表。

    my_dict = {'a': 1, 'b': 2, 'c': 3}
    
    for key in list(my_dict.keys()):
        print(key)
        if key == 'b':
            del my_dict[key]
    
    print(my_dict)  # ️ {'a': 1, 'c': 3}
    

这完全是与原始答案重复的。 - Karl Knechtel

0
对于这种情况,我喜欢制作一个深拷贝,并在修改原始“dict”时循环遍历该副本。
如果查找字段在列表中,则可以在列表的for循环中进行枚举,然后将位置指定为索引以访问原始dict中的字段。

0
你可以创建一个函数来实现这个功能。

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


-1

嵌套的空值

假设我们有一个带有嵌套键的字典,其中一些键是空值:

dicti = {
"k0_l0":{
    "k0_l1": {
        "k0_l2": {
                "k0_0":None,
                "k1_1":1,
                "k2_2":2.2
                }
        },
        "k1_l1":None,
        "k2_l1":"not none",
        "k3_l1":[]
    },
    "k1_l0":"l0"
}

然后我们可以使用这个函数来删除空值:

def pop_nested_nulls(dicti):
    for k in list(dicti):
        if isinstance(dicti[k], dict):
            dicti[k] = pop_nested_nulls(dicti[k])
        elif not dicti[k]:
            dicti.pop(k)
    return dicti

pop_nested_nulls(dicti)的输出

{'k0_l0': {'k0_l1': {'k0_l2': {'k1_1': 1,
                               'k2_2': 2.2}},
           'k2_l1': 'not '
                    'none'},
 'k1_l0': 'l0'}

这实际上是在创造一个新问题来解决。原帖中的问题与“去除空值”根本没有任何特定关系;这只是遇到同类问题的无数例子之一。当然,可以使用递归来处理嵌套结构;但这与主题完全无关。 - Karl Knechtel

-1
如果字典中的值也是唯一的,可以使用这种方法:
keyToBeDeleted = None
for k, v in mydict.items():
    if(v == match):
        keyToBeDeleted = k
        break
mydict.pop(keyToBeDeleted, None)

1
这里有一个有用的想法,但实现方式是针对与OP的示例不同的问题。最好提供一种通用技术(例如,展示带有某些占位符来决定是否应该删除项的代码,并展示可以删除多个项的代码),并*解释它是如何解决问题的。然而,Rohit在2018年的回答已经完全展示了这一点。 - Karl Knechtel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接