【问题标题】:How to avoid "RuntimeError: dictionary changed size during iteration" error?如何避免“RuntimeError:迭代期间字典更改大小”错误?
【发布时间】:2021-07-05 14:48:48
【问题描述】:

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

我有一本列表字典:

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

其中一些值为空。在创建这些列表结束时,我想在返回我的字典之前删除这些空列表。目前我正在尝试这样做:

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

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

【问题讨论】:

    标签: python list dictionary loops


    【解决方案1】:

    在 Python 3.x 和 2.x 中,您可以使用 list 强制生成密钥副本:

    for i in list(d):
    

    在 Python 2.x 中调用 keys 制作了一个密钥副本,您可以在修改 dict 时对其进行迭代:

    for i in d.keys():
    

    但请注意,在 Python 3.x 中,第二种方法对您的错误没有帮助,因为 keys 返回一个 view object 而不是将键复制到列表中。

    【讨论】:

    • 我相信您的意思是“调用 keys 会复制您可以迭代的 keys”,也就是 plural 正确的键?否则如何迭代一个键?顺便说一句,我不是在挑剔,我真的很想知道这是否确实是关键或关键
    • 或者使用元组而不是列表,因为它更快。
    • 为了阐明 python 3.x 的行为,d.keys() 返回一个 iterable (不是迭代器),这意味着它是直接对字典键的视图。使用for i in d.keys() 实际上确实在python 3.x 一般 中工作,但是因为它正在迭代字典键的可迭代视图,所以在循环期间调用d.pop() 会导致与您相同的错误成立。 for i in list(d) 模拟了在迭代之前将键复制到列表中的效率稍低的 python 2 行为,适用于像您这样的特殊情况。
    • 在 python3.x 中,list(d.keys()) 创建与list(d) 相同的输出,在dict 上调用list 返回键。 keys 电话(虽然不是那么贵)是不必要的。
    • 令人惊讶的是,items() doc 声称“返回字典的(键,值)对列表的副本。”,但在弹出项目时循环通过 items() 仍然给出 RuntimeError:字典在迭代期间改变了大小.
    【解决方案2】:

    你只需要使用“复制”:

    这样您就可以遍历原始字典字段,并且可以动态更改所需的字典(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]}
    

    【讨论】:

      【解决方案3】:

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

      >>> 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]}
      

      【讨论】:

      • d.iteritems() 给了我一个错误。我改用d.items() - 使用python3
      • 这适用于 OP 问题中提出的问题。但是,任何在多线程代码中遇到此 RuntimeError 后来到这里的人,请注意 CPython 的 GIL 也可以在列表理解的中间释放,您必须以不同的方式修复它。
      【解决方案4】:

      这对我有用:

      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

      【讨论】:

      • 迄今为止,在此线程上列出的所有选项中,最快速、最简单的修复。谢谢@singrium
      【解决方案5】:

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

      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:最后一个选项很有趣,因为它只复制那些需要删除的项目的键。如果只需要删除相对于字典大小的少量项目,这可能会提供更好的性能。
      • @MarkByers 是的 - 如果有很多,那么将字典重新绑定到一个经过过滤的新字典是一个更好的选择。结构应该如何工作始终是人们的期望
      • 重新绑定的一个危险是,如果程序中的某个地方有一个对象持有对旧字典的引用,它就不会看到更改。如果您确定不是这种情况,那么确定...这是一种合理的方法,但重要的是要了解它与修改原始字典并不完全相同。
      • @MarkByers 非常好的观点 - 你和我都知道(以及无数其他人),但这对所有人来说并不明显。而且我会把钱放在桌子上,它还没有在后面咬你:)
      • 避免插入空条目这一点非常好。
      【解决方案6】:

      对于 Python 3:

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

      【讨论】:

      • 简洁明了。在 Python 2.7 中也为我工作。
      【解决方案7】:

      在 for 循环期间更改字典时,您不能遍历字典。进行强制转换以列出并迭代该列表,它对我有用。

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

      【讨论】:

        【解决方案8】:

        当您尝试删除某些键时,避免“迭代错误期间字典更改大小”, 只需将 '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)
        

        +++ 输出:

        {'k1': 1, 'k2': 2, 'k3': 3, 'k4': 4}

        {'k1': 1, 'k3': 3}

        +++

        这只是示例,可根据您的情况/要求进行更改, 我希望这会有所帮助。

        【讨论】:

          【解决方案9】:

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

          for i in x.keys():
          

          for i in list(x)
          

          【讨论】:

          • 这个技巧对我有用,谢谢。
          【解决方案10】:

          运行时错误的原因是当数据结构在迭代过程中发生变化时,您无法迭代数据结构。

          实现您要查找的内容的一种方法是使用 list 附加要删除的键,然后在遍历列表时使用字典上的 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)
          

          【讨论】:

            【解决方案11】:
            dictc={"stName":"asas"}
            keys=dictc.keys()
            for key in list(keys):
                dictc[key.upper()] ='New value'
            print(str(dictc))
            

            【讨论】:

              【解决方案12】:

              对于这样的情况,我喜欢做一个深拷贝,并在修改原始字典的同时循环遍历该拷贝。

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

              【讨论】:

                猜你喜欢
                • 2021-11-21
                • 2019-12-02
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多