如何在迭代时从字典中删除项目?

268 python scripting dictionary

在迭代它时从Python中删除字典中的项是否合法?

例如:

for k, v in mydict.iteritems():
   if k == val:
     del mydict[k]
Run Code Online (Sandbox Code Playgroud)

我们的想法是从字典中删除不符合某个条件的元素,而不是创建一个新字典,该字典是被迭代的字典的子集.

这是一个好的解决方案吗?有更优雅/有效的方式吗?

Bla*_*air 288

编辑:

这个答案不适用于Python3,并且会给出一个RuntimeError.

RuntimeError:字典在迭代期间改变了大小.

发生这种情况是因为mydict.keys()返回迭代器而不是列表.正如评论中所指出的,只需转换mydict.keys()为列表list(mydict.keys()),它应该可以工作.


控制台中的一个简单测试表明,在迭代它时无法修改字典:

>>> mydict = {'one': 1, 'two': 2, 'three': 3, 'four': 4}
>>> for k, v in mydict.iteritems():
...    if k == 'two':
...        del mydict[k]
...
------------------------------------------------------------
Traceback (most recent call last):
  File "<ipython console>", line 1, in <module>
RuntimeError: dictionary changed size during iteration
Run Code Online (Sandbox Code Playgroud)

正如delnan的回答所述,当迭代器尝试移动到下一个条目时,删除条目会导致问题.相反,使用该keys()方法获取密钥列表并使用它:

>>> for k in mydict.keys():
...    if k == 'two':
...        del mydict[k]
...
>>> mydict
{'four': 4, 'three': 3, 'one': 1}
Run Code Online (Sandbox Code Playgroud)

如果需要根据项值删除,请改用该items()方法:

>>> for k, v in mydict.items():
...     if v == 3:
...         del mydict[k]
...
>>> mydict
{'four': 4, 'one': 1}
Run Code Online (Sandbox Code Playgroud)

  • 要详细说明@max的详细说明,如果您使用2to3转换上述代码,它将起作用.其中一个默认修复程序将使循环看起来像`for k,v in list(mydict.items()):`在Python 3中工作正常.相同的`keys()`变成`list(keys())` . (92认同)
  • 详细说明@TimLesher评论......这在Python 3中不起作用. (72认同)
  • 请注意,在Python 3中,dict.items()返回一个迭代器(并且dict.iteritems()已经消失). (50认同)
  • Walter指出,@TomášZato对于python3,你需要在列表中使用`for k(mydict.keys()):`因为python3使得keys()方法成为迭代器,并且在迭代期间也不允许删除dict项.通过添加list()调用,可以将keys()迭代器转换为列表.因此,当您处于for循环体内时,您不再迭代字典本身. (12认同)
  • 这不起作用.我收到一个错误:`RuntimeError:字典在迭代期间改变了大小` (7认同)
  • 我会选择“for key, value in my_dict.copy().items():”,这样您就可以迭代重复的字典并从原始字典中删除。它保持数据输入的整洁 (3认同)

Joc*_*zel 85

您也可以分两步完成:

remove = [k for k in mydict if k == val]
for k in remove: del mydict[k]
Run Code Online (Sandbox Code Playgroud)

我最喜欢的方法通常是制作一个新的词典:

# Python 2.7 and 3.x
mydict = { k:v for k,v in mydict.items() if k!=val }
# before Python 2.7
mydict = dict((k,v) for k,v in mydict.iteritems() if k!=val)
Run Code Online (Sandbox Code Playgroud)

  • @senderle:实际上是2.7. (11认同)
  • 字典理解方法制作了字典的副本; 幸运的是,价值至少不会被深层复制,只是链接.如果你有很多钥匙,它可能会很糟糕.出于这个原因,我更喜欢`remove`循环方法. (5认同)
  • 值得注意的是,虽然这样的"字典理解"只适用于python 3 ... (2认同)
  • 您还可以组合以下步骤:`for k in [k for k in mydict if k == val]: del mydict[k]` (2认同)

小智 19

迭代时无法修改集合.这种方式就是疯狂 - 最值得注意的是,如果你被允许删除并删除当前项目,迭代器必须继续(+1),下一次调用next将超过(+2),所以你会最后跳过一个元素(你删除的元素后面的元素).您有两种选择:

  • 复制所有键(或值,或两者,具体取决于您的需要),然后迭代这些键.您可以使用.keys()et al(在Python 3中,将生成的迭代器传递给list).但是,在空间方面可能非常浪费.
  • mydict像往常一样迭代,在单独的集合中保存要删除的键to_delete.当您完成迭代mydict,删除所有项目to_deletemydict.在第一种方法中保存一些(取决于删除的键数量和保留的数量)空间,但还需要更多行.

  • @Nils它不会抛出异常,但它仍然不正确.观察:http://codepad.org/Yz7rjDVT - 请参阅http://stackoverflow.com/q/6260089/395760以获得解释 (3认同)

Ign*_*ams 17

迭代副本,例如返回的副本items():

for k, v in list(mydict.items()):
Run Code Online (Sandbox Code Playgroud)

  • 在Python 3下,`dict.items()`返回一个迭代器而不是一个副本.请参阅[Blair]的评论(/sf/users/46816521/)的[答案](/sf/answers/376955281/),其中(遗憾地)也假设Python 2语义. (3认同)
  • @Josh:这完全取决于你需要使用"v"作为删除标准. (2认同)

rsa*_*den 10

使用起来最干净list(mydict)

>>> mydict = {'one': 1, 'two': 2, 'three': 3, 'four': 4}
>>> for k in list(mydict):
...     if k == 'three':
...         del mydict[k]
... 
>>> mydict
{'four': 4, 'two': 2, 'one': 1}
Run Code Online (Sandbox Code Playgroud)

这对应于列表的并行结构:

>>> mylist = ['one', 'two', 'three', 'four']
>>> for k in list(mylist):                            # or mylist[:]
...     if k == 'three':
...         mylist.remove(k)
... 
>>> mylist
['one', 'two', 'four']
Run Code Online (Sandbox Code Playgroud)

两者都在python2和python3中工作。


gli*_*ihm 8

使用python3,迭代dic.keys()将引发字典大小错误.您可以使用以下替代方式:

使用python3测试,它工作正常,并且不会引发错误" 字典在迭代期间改变大小 ":

my_dic = { 1:10, 2:20, 3:30 }
# Is important here to cast because ".keys()" method returns a dict_keys object.
key_list = list( my_dic.keys() )

# Iterate on the list:
for k in key_list:
    print(key_list)
    print(my_dic)
    del( my_dic[k] )


print( my_dic )
# {}
Run Code Online (Sandbox Code Playgroud)

我使用它时,从使用大量内存的字典中,我想构建另一个字典(包含第一个字体的修改)而不做"复制"并重载RAM.


小智 6

您可以使用字典理解。

d = {k:d[k] for k in d if d[k] != val}

  • 但它创建了一个新字典,而不是就地修改“d”。 (4认同)