在遍历 Python 命令行时修改它

假设我们有一个 Python 字典 d,我们这样迭代它:

for k, v in d.iteritems():
del d[f(k)] # remove some item
d[g(k)] = v # add a new item

(f and g are just some black-box transformations.)

In other words, we try to add/remove items to d while iterating over it using iteritems.

这个定义明确吗? 你能提供一些参考资料来支持你的答案吗?


另请参阅 如何避免“ RuntimeError: 字典在迭代过程中改变了大小”错误?了解如何避免该问题的单独问题。

103676 次浏览

您不能这样做,至少在 d.iteritems()中不能

RuntimeError: dictionary changed size during iteration

如果您使用 d.items(),那么它就可以工作。

在 Python3中,d.items()是字典中的一个视图,就像 Python2中的 d.iteritems()一样。要在 Python3中执行此操作,请改为使用 d.copy().items()。这同样允许我们迭代字典的一个副本,以避免修改我们正在迭代的数据结构。

下面的代码表明,这没有得到很好的定义:

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

The first example calls g(k), and throws an exception (dictionary changed size during iteration).

第二个示例调用 h (k)并且不抛出异常,但是输出:

{21: 'axx', 22: 'bxx', 23: 'cxx'}

从代码来看,这似乎是错误的——我原本以为会是这样的:

{11: 'ax', 12: 'bx', 13: 'cx'}

Alex Martelli weighs in on this 给你.

在循环遍历容器时更改容器(例如 dict)可能不安全。 所以 del d[f(k)]可能不安全。正如您所知,解决方案是使用 d.copy().items()(循环遍历容器的独立副本) ,而不是使用 d.iteritems()d.items()(它们使用相同的底层容器)。

可以修改 dict 的 存在索引处的值,但是在新索引处插入值(例如 d[g(k)] = v)可能不起作用。

在 Python doc 页面(针对 Python 2.7)中明确提到

在字典中添加或删除条目时使用 iteritems()可能会产生一个 RuntimeError,或者不能迭代所有条目。

巨蟒3也是如此。

对于 iter(d)d.iterkeys()d.itervalues()也是如此,我只能说对于 for k, v in d.items():也是如此(我不记得 for具体做了什么,但是如果实现称为 iter(d),我不会感到惊讶)。

I got the same problem and I used following procedure to solve this issue.

即使在对 Python List 进行迭代期间进行修改,也可以对其进行迭代。 因此,对于下面的代码,它将打印1的无穷大。

for i in list:
list.append(1)
print 1

因此,使用 list 和 dict 协同工作可以解决这个问题。

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))

I have a large dictionary containing Numpy arrays, so the dict.copy().keys() thing suggested by @murgatroid99 was not feasible (though it worked). Instead, I just converted the keys_view to a list and it worked fine (in Python 3.4):

for item in list(dict_d.keys()):
temp = dict_d.pop(item)
dict_d['some_key'] = 1  # Some value

我意识到这并没有像上面的答案那样深入 Python 内部工作的哲学领域,但是它确实为所陈述的问题提供了一个实际的解决方案。

今天我有一个类似的用例,但是不是简单地在循环开始时具体化字典上的键,我希望对 dict 的更改能够影响 dict 的迭代,这是一个有序的 dict。

我最终建立了以下例行程序,也可以是 在 jaraco.itertools 中找到:

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)

这个函数可以代替上面的 d.iteritems()来达到预期的效果。

Python 3你只需要:

prefix = 'item_'
t = {'f1': 'ffw', 'f2': 'fca'}
t2 = dict()
for k,v in t.items():
t2[k] = prefix + v

or use:

t2 = t1.copy()

You should never modify original dictionary, it leads to confusion as well as potential bugs or RunTimeErrors. Unless you just append to the dictionary with new key names.

这个问题询问如何使用迭代器 (有趣的是,Python2.iteritems迭代器在 Python3中不再受支持)来删除或添加项目,以及如何在已接受的答案中找到 它必须有一个 < em > No 作为它的唯一正确答案。然而: 大多数搜索者试图找到一个解决方案,他们不会关心这是如何在技术上完成的,无论是迭代器还是递归,有 的问题的解决方案:

如果不使用附加(递归)函数,则不能循环更改 dict。

因此,这个问题应该与一个有工作解决办法的问题联系起来:

By the same recursive methods, you will also able to add items as the question asks for as well.


由于我链接这个问题的请求被拒绝,这里有一个解决方案的副本,可以删除从一个结果项目。见 在深度嵌套的字典中,只要所选的键出现在任何位置,我如何删除键: 值对?(= “删除”)的例子/学分/注释。

import copy


def find_remove(this_dict, target_key, bln_overwrite_dict=False):
if not bln_overwrite_dict:
this_dict = copy.deepcopy(this_dict)


for key in this_dict:
# if the current value is a dict, dive into it
if isinstance(this_dict[key], dict):
if target_key in this_dict[key]:
this_dict[key].pop(target_key)


this_dict[key] = find_remove(this_dict[key], target_key)


return this_dict


dict_nested_new = find_remove(nested_dict, "sub_key2a")

诀窍

诀窍是在递归到达子级别之前,提前查明 target _ key 是否在下一个子级别中(= this _ dict [ key ] = 当前 dict 迭代的值)。只有这样,在迭代字典时,仍然可以删除子级的键: 值对。一旦你达到了要删除的密钥的相同级别,然后尝试从那里删除它,你会得到错误:

RuntimeError: dictionary changed size during iteration

递归解决方案仅在下一个值的子级上进行任何更改,因此避免了错误。