在迭代它时修改Python字典

时间:2011-07-21 14:15:59

标签: 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

fg只是一些黑盒转换。)

换句话说,我们尝试在使用d进行迭代时向iteritems添加/删除项目。

这个定义得很好吗?你能提供一些参考来支持你的答案吗?

(如果它被打破了,很明显如何解决这个问题,所以这不是我追求的角度。)

8 个答案:

答案 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。除非您只是用新的键名附加到字典上。