【问题标题】:RuntimeError: OrderedDict mutated during iterationRuntimeError:OrderedDict 在迭代期间发生了突变
【发布时间】:2018-03-04 14:06:39
【问题描述】:

我对 python3 比较陌生,我正在尝试遍历现有的 OrderedDict() 以删除以 None 为值的条目。在 python2 中这不是问题,但据我了解,删除 dict.iteritems() (等等...)是由于返回 dict.items() 的方式发生了一些变化。

reeeeeeeeeeally 想避免复制字典...

我将要做(可能数百)数千个这样的操作,并且我不想将我使用的内存量增加一倍,只是为了从 OrderedDict 中删除空条目。

这是引发错误的代码:

class DefaultHeaders(OrderedDict):
    def __init__(self, loop=None):
        super(DefaultHeaders, self).__init__()

        self['User-Agent'] = "Mozilla/5.0 (Windows NT 6.2; Win64; x64; rv:16.0.1) Gecko/20121011 Firefox/16.0.1" # <--(dummy User-Agent header for consistent response-format)
        self['X-Search-ClientIP'] = gethostbyname(gethostname())                                                 
        self['X-MSEdge-ClientID'] = None
        self['Accept'] = None
        self['Accept-Language'] = None
        self['X-Search-Location'] = None

        self._clean1() # <--raises error
        # self._clean2() # <--raises error
        # self._clean3() # <--raises error

    def _clean1(self):
        for k, v in self.items():
            if k in ('count', 'offset'):
                pass
            elif not v: del self[k]

    def _clean2(self):
        for k, v in list(self.items()):
            if k in ('count', 'offset'):
                pass
            elif not v: del self[k]


    def _clean3(self):
        _iter_this = list(self.items())
        for k, v in _iter_this:
            if k in ('count', 'offset'):
                pass
            elif not v: del self[k]

这是我得到的错误:

...
    for k, v in self.items():
RuntimeError: OrderedDict mutated during iteration

Process finished with exit code 1

【问题讨论】:

  • 复制 OrderedDict 不会使内存使用量翻倍。这只会复制 OrderedDict 本身,而不是它的键或值。复制它。
  • 你能扩展一下吗?实例化一个新的类实例不会带来开销吗?
  • "实例化一个新的类实例不会带来开销吗?" - 是的,但是这个开销比你想象的要小得多。
  • 另外,_clean2_clean3 不应该引发错误。
  • 当你清理字典时,你会得到一个副本。完成清理后,可以丢弃副本并回收其内存。由于您一次只会清理一个字典,因此您不需要一次保留多个字典的副本。

标签: python python-3.x dictionary iteration ordereddictionary


【解决方案1】:

(>_

def _clean(self):
    for k in list(self.keys()):
        if k in ('count', 'offset'):
            pass
        elif not self[k]:
            del self[k]

我还意识到复制不是一种选择。我需要重新分配 self 并创建我的类调用的新实例 _clean() 产生无限递归。

【讨论】:

  • 列表理解是一次分配,list() 调用基本上是创建整个字典的副本,in 是 O(n) 查找。我会说这无济于事;)
  • 不能那样做,扎克。在迭代 ordered_dict 时调用 pop(k) 会引发错误
  • @liborm:唯一的in 查找位于固定长度为2 的元组中,所以这可能没问题。 for k, v in 中的 in 是迭代,而不是查找。
  • @RobTruxal 好吧,不仅如此,而且您正在使用字典理解来处理副作用,您实际上是在创建一个 弹出键列表,然后将其丢弃,一切无缘无故。使用 for 循环会更好,for k,v in list(d.items()): &lt;pop stuff&gt;
  • @RobTruxal 好吧,即使没有,你仍然会填充一个完全无用的 Nones 列表
【解决方案2】:

用索引迭代字典怎么样?:

    def _clean4(self):
        i = 0

        while i < len(self):
            if self.keys()[i] not in ('count', 'offset') and not self[self.keys()[i]]:
                del self[self.keys()[i]]
            else:
                i = i + 1

【讨论】:

  • 你不能在 Python 3 和 Python 2 上索引 self.keys(),回到你可以索引 self.keys() 的时候,这是因为该函数每次调用它时都会创建一个完整的列表。
【解决方案3】:

你试过了吗:

[ordered_dict.pop(k) for k in ordered_dict if k not in ['count', 'offset'] or ordered_dict[k] == None]

【讨论】:

  • 这更不对。它在迭代它时仍然会改变字典,但现在它甚至不检查值。
  • 所以你是说理解不会对所有的键都起作用?我不同意,因为 [k for k in ordered_dict] 在开始迭代之前创建了一个列表。这样,无论是否已“弹出”,都会检查每个键。 Dict 是一个哈希,所以不能有 dup 键。每个键都会被评估,可能会被弹出,然后迭代继续进行,不管发生了什么。
  • 对@user2357112 使用“错误”一词高五。大粉丝。
猜你喜欢
  • 2019-03-04
  • 1970-01-01
  • 2019-02-14
  • 2022-01-13
  • 1970-01-01
  • 2019-11-17
  • 1970-01-01
  • 2021-10-29
  • 2015-03-31
相关资源
最近更新 更多