Modifying a Python dict while iterating over it
假设我们有一个python字典
1 2 3 | for k,v in d.iteritems(): del d[f(k)] # remove some item d[g(k)] = v # add a new item |
(
换句话说,我们尝试在使用
这个定义清楚吗?你能提供一些参考资料来支持你的答案吗?
(很明显,如果它坏了,如何修复它,所以这不是我想要的角度。)
在python文档页面(对于python 2.7)中明确提到
Using
iteritems() while adding or deleting entries in the dictionary may raise aRuntimeError or fail to iterate over all entries.
号
与Python3类似。
对于
亚历克斯·马泰利在这里说了算。
在容器上循环时更换容器(如dict)可能不安全。因此,
可以修改dict的现有索引的值,但在新索引(如
你不能这样做,至少用
1 | RuntimeError: dictionary changed size during iteration |
如果您使用
在python 3中,
下面的代码显示,这一点没有很好地定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | def f(x): return x def g(x): return x+1 def h(x): return x+10 try: d = {1:"a", 2:"b", 3:"c"} for k, v in d.iteritems(): del d[f(k)] d[g(k)] = v+"x" print d except Exception as e: print"Exception:", e try: d = {1:"a", 2:"b", 3:"c"} for k, v in d.iteritems(): del d[f(k)] d[h(k)] = v+"x" print d except Exception as e: print"Exception:", e |
号
第一个示例调用g(k),并抛出一个异常(迭代期间字典更改了大小)。
第二个示例调用h(k),不引发异常,但输出:
1 | {21: 'axx', 22: 'bxx', 23: 'cxx'} |
从代码上看,这似乎是错误的——我本以为会出现如下情况:
1 | {11: 'ax', 12: 'bx', 13: 'cx'} |
。
我有一个包含numpy数组的大字典,所以@murgatroid99建议的dict.copy().keys()方法是不可行的(尽管它有效)。相反,我只是将keys_视图转换为一个列表,它工作得很好(在Python3.4中):
1 2 3 | for item in list(dict_d.keys()): temp = dict_d.pop(item) dict_d['some_key'] = 1 # Some value |
。
我意识到这并不像上面的答案那样深入到Python内部工作的哲学领域,但它确实为所述问题提供了一个实际的解决方案。
我有同样的问题,我用下面的程序来解决这个问题。
即使在迭代过程中进行了修改,也可以对python列表进行迭代。所以对于下面的代码,它将无限地打印1。
1 2 3 | for i in list: list.append(1) print 1 |
因此,协同使用list和dict可以解决这个问题。
1 2 3 4 5 6 7 | d_list=[] d_dict = {} for k in d_list: if d_dict[k] is not -1: d_dict[f(k)] = -1 # rather than deleting it mark it with -1 or other value to specify that it will be not considered further(deleted) d_dict[g(k)] = v # add a new item d_list.append(g(k)) |
。
python 3您应该:
1 2 3 4 5 | prefix = 'item_' t = {'f1': 'ffw', 'f2': 'fca'} t2 = dict() for k,v in t.items(): t2[k] = prefix + v |
。
或使用:
1 | t2 = t1.copy() |
您不应该修改原始字典,它会导致混淆以及潜在的错误或运行时错误。除非用新的键名附加到字典中。
今天我有一个类似的用例,但是我不想在循环开始时简单地将字典上的键具体化,而是希望对dict进行更改以影响dict的迭代,这是一个有序的dict。
我最终建立了以下程序,也可以在jaraco.itertools中找到:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | def _mutable_iter(dict): """ Iterate over items in the dict, yielding the first one, but allowing it to be mutated during the process. >>> d = dict(a=1) >>> it = _mutable_iter(d) >>> next(it) ('a', 1) >>> d {} >>> d.update(b=2) >>> list(it) [('b', 2)] """ while dict: prev_key = next(iter(dict)) yield prev_key, dict.pop(prev_key) |
docstring说明了用法。该功能可代替上述