假设我们有一个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
只是一些黑盒转换。)
换句话说,我们尝试在使用d
进行迭代时向iteritems
添加/删除项目。
这个定义得很好吗?你能提供一些参考来支持你的答案吗?
(如果它被打破了,很明显如何解决这个问题,所以这不是我追求的角度。)
答案 0 :(得分:44)
在Python文档页面(Python 2.7)中明确提到了
在字典中添加或删除条目时使用
iteritems()
可能会引发RuntimeError
或无法迭代所有条目。
同样适用于Python 3。
同样适用于iter(d)
,d.iterkeys()
和d.itervalues()
,我会尽可能地说for k, v in d.items():
(我记不起来了) for
做了什么,但如果实现调用iter(d)
),我不会感到惊讶。
答案 1 :(得分:43)
Alex Martelli对此here进行了评估。
在环绕容器时更换容器(例如dict)可能不安全。
所以del d[f(k)]
可能不安全。如您所知,解决方法是使用d.items()
(循环容器的独立副本)而不是d.iteritems()
(使用相同的基础容器)。
可以修改dict的现有索引处的值,但在新索引处插入值(例如d[g(k)]=v
)可能不起作用。
答案 2 :(得分:22)
你不能这样做,至少用d.iteritems()
。我尝试过,而Python失败了
RuntimeError: dictionary changed size during iteration
如果你改为使用d.items()
,那么它就可以了。
在Python 3中,d.items()
是字典的视图,如Python 2中的d.iteritems()
。要在Python 3中执行此操作,请使用d.copy().items()
。这同样允许我们迭代字典的副本,以避免修改我们迭代的数据结构。
答案 3 :(得分:6)
以下代码显示这个定义不明确:
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'}
答案 4 :(得分:5)
我有一个包含Numpy数组的大字典,所以@ murgatroid99建议的dict.copy()。keys()事情是不可行的(尽管它有效)。相反,我只是将keys_view转换为一个列表,它工作正常(在Python 3.4中):
for item in list(dict_d.keys()):
temp = dict_d.pop(item)
dict_d['some_key'] = 1 # Some value
我意识到这并没有深入到Python的内部工作的哲学领域,如上面的答案,但它确实为所述问题提供了实际的解决方案。
答案 5 :(得分:1)
我遇到了同样的问题,我使用了以下程序来解决这个问题。
即使您在迭代过程中进行修改,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))
答案 6 :(得分:0)
今天,我有一个类似的用例,但我不想在循环开始时简单地在字典上实现键,而是希望对dict进行更改以影响dict的迭代,这是一个有序的dict。 / p>
我最终建立了以下例程,该例程也可以是found in 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()
使用,以达到预期的效果。
答案 7 :(得分:0)
Python 3您应该:
prefix = 'item_'
t = {'f1': 'ffw', 'f2': 'fca'}
t2 = dict()
for k,v in t.items():
t2[k] = prefix + v
或使用:
t2 = t1.copy()
您永远不要修改原始词典,否则会导致混乱以及潜在的错误或RunTimeErrors。除非您只是用新的键名附加到字典上。