在迭代Python dict时修改它
假设我们有一个Python字典在迭代Python dict时修改它,python,dictionary,Python,Dictionary,假设我们有一个Python字典d,我们像这样对它进行迭代: for k,v in d.iteritems(): del d[f(k)] # remove some item d[g(k)] = v # add a new item (f和g只是一些黑盒变换。) 换句话说,我们尝试在使用iteritems对d进行迭代的同时,向其添加/删除项 这个定义清楚吗?你能提供一些参考资料来支持你的回答吗 (很明显,如果它坏了,如何修复它,所以这不是我想要的角度。)你不能这样做,至少用d.i
d
,我们像这样对它进行迭代:
for k,v in d.iteritems():
del d[f(k)] # remove some item
d[g(k)] = v # add a new item
(f
和g
只是一些黑盒变换。)
换句话说,我们尝试在使用iteritems
对d
进行迭代的同时,向其添加/删除项
这个定义清楚吗?你能提供一些参考资料来支持你的回答吗
(很明显,如果它坏了,如何修复它,所以这不是我想要的角度。)你不能这样做,至少用
d.iteritems()
。我试过了,但是Python失败了
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
第一个示例调用g(k),并抛出一个异常(字典在迭代过程中更改了大小)
第二个示例调用h(k)并没有抛出异常,但输出:
{21: 'axx', 22: 'bxx', 23: 'cxx'}
从代码上看,这似乎是错误的-我本以为会是这样的:
{11: 'ax', 12: 'bx', 13: 'cx'}
亚历克斯·马泰利也参与其中 在容器上循环时,更换容器(如dict)可能不安全。 因此
deld[f(k)]
可能不安全。如您所知,解决方法是使用d.items()
(在容器的独立副本上循环)而不是d.iteritems()
(使用相同的底层容器)
可以修改dict现有索引处的值,但在新索引处插入值(例如,
d[g(k)]=v
)可能不起作用。Python文档页面(for)中明确提到
在字典中添加或删除条目时使用iteritems()
同样地,对于
对于iter(d)
、d.iterkeys()
和d.itervalues()
,我甚至可以说它对中的k,v都有作用。items():
(我记不清对于有什么作用,但如果实现调用iter(d)
,我不会感到惊讶).我遇到了同样的问题,我使用以下步骤解决了这个问题
Python列表可以被迭代,即使您在迭代过程中对其进行了修改。
所以对于下面的代码,它将无限打印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))
我有一个包含Numpy数组的大字典,所以@murgatroid99建议的dict.copy().keys()方法是不可行的(尽管它有效)。相反,我只是将keys_视图转换为一个列表,它工作得很好(在Python 3.4中):
我意识到这并不像上面的答案那样深入到Python内部工作的哲学领域,但它确实为所述问题提供了一个实用的解决方案。今天我有一个类似的用例,但不是简单地在循环开始时具体化字典上的键,我希望对dict的更改能够影响dict的迭代,这是一个有序dict
我最终构建了以下例程,也可以是:
docstring说明了该用法。此函数可以用来代替上面的d.iteritems()
,以达到预期效果。Python 3您应该:
prefix = 'item_'
t = {'f1': 'ffw', 'f2': 'fca'}
t2 = dict()
for k,v in t.items():
t2[k] = prefix + v
或使用:
t2 = t1.copy()
您永远不应该修改原始字典,它会导致混乱以及潜在的bug或运行时错误。除非您只是在字典中添加新的键名。可能重复的See我已经尝试过这样做,如果您保持初始dict大小不变,例如,替换任何键/值而不是删除它们,则此代码不会抛出异常。我不同意“很明显,如果它损坏了,如何修复它”对于搜索这个主题的每个人(包括我自己),我希望被接受的答案至少涉及到这一点。我可以理解为什么你可能期望{11:ax',12:bx',13:cx'}
,但是21,22,23应该给你关于实际发生了什么的线索:你的循环通过了项目1,2,3,11,12,13但在第二轮新项目插入到您已经迭代过的项目之前时,没有成功地拾取它们。更改h()
以返回x+5
,您将获得另一个x:'axxx'
等或'x+3',您将获得华丽的'axxxxx'
是的,我恐怕是我的错误-正如您所说,我的预期输出是{11:'ax 12:'bx',13:'cx'}
,因此我将更新我的帖子。无论哪种方式,这显然都不是定义良好的行为。我在回答中添加了Python 3。仅供参考,Py2的d.items()
到Py3的直译(例如2to3
)是list(d.items())
,尽管d.copy().items()
可能具有相当的效率。如果dict对象非常大,则是d.copy().items>()efficiet?我想这对我来说是一个关键答案。很多用例都会有一个过程插入东西,另一个过程清理/删除它们,所以建议使用d.items()Python 3注意事项与Python 3注意事项相关的更多信息可以在前面提到的Python 2 dict方法的语义等价物中找到。“可以修改dict现有索引处的值”--你有这方面的参考资料吗?@JonathonReinhart:没有,我没有这方面的参考资料,但我认为这在Python中是相当标准的。例如,Alex Martelli是一名Python核心开发人员。为了社区的利益,我会说我使用了非常多的代码片段。我想,既然我没有遇到运行时错误
prefix = 'item_'
t = {'f1': 'ffw', 'f2': 'fca'}
t2 = dict()
for k,v in t.items():
t2[k] = prefix + v
t2 = t1.copy()