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

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

在Python上进行迭代时从字典中删除项目是否合法?

例如:

for k, v in mydict.iteritems():
   if k == val:
     del mydict[k]

这个想法是从字典中删除不满足特定条件的元素,而不是创建一个新字典,该字典是被迭代的字典的子集。

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

Is it legitimate to delete items from a dictionary in Python while iterating over it?

For example:

for k, v in mydict.iteritems():
   if k == val:
     del mydict[k]

The idea is to remove elements that don’t meet a certain condition from the dictionary, instead of creating a new dictionary that’s a subset of the one being iterated over.

Is this a good solution? Are there more elegant/efficient ways?


回答 0

编辑:

此答案不适用于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

如delnan的回答所述,当迭代器尝试移至下一个条目时,删除条目会导致问题。而是使用keys()方法获取键列表并进行处理:

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

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

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

EDIT:

This answer will not work for Python3 and will give a RuntimeError.

RuntimeError: dictionary changed size during iteration.

This happens because mydict.keys() returns an iterator not a list. As pointed out in comments simply convert mydict.keys() to a list by list(mydict.keys()) and it should work.


A simple test in the console shows you cannot modify a dictionary while iterating over it:

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

As stated in delnan’s answer, deleting entries causes problems when the iterator tries to move onto the next entry. Instead, use the keys() method to get a list of the keys and work with that:

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

If you need to delete based on the items value, use the items() method instead:

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

回答 1

您也可以分两个步骤进行操作:

remove = [k for k in mydict if k == val]
for k in remove: del mydict[k]

我最喜欢的方法通常是做出一个新的决定:

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

You could also do it in two steps:

remove = [k for k in mydict if k == val]
for k in remove: del mydict[k]

My favorite approach is usually to just make a new dict:

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

回答 2

迭代时不能修改集合。那就是疯狂-最为明显的是,如果允许您删除和删除当前项目,则迭代器将必须继续(+1),下一次调用next将使您超出该范围(+2),因此您会最终跳过了一个元素(删除的元素后面的一个)。您有两种选择:

  • 复制所有键(或值,或两者,取决于您的需要),然后遍历这些键。您可以.keys()为此使用et al(在Python 3中,将生成的迭代器传递给list)。但是在空间上可能会非常浪费。
  • mydict照常进行迭代,将要保存的密钥保存在单独的collection中to_delete。当你完成迭代mydict,删除所有项目to_deletemydict。与第一种方法相比,可以节省一些(取决于删除的键数和剩余的键数)空间,但还需要多几行。

You can’t modify a collection while iterating it. That way lies madness – most notably, if you were allowed to delete and deleted the current item, the iterator would have to move on (+1) and the next call to next would take you beyond that (+2), so you’d end up skipping one element (the one right behind the one you deleted). You have two options:

  • Copy all keys (or values, or both, depending on what you need), then iterate over those. You can use .keys() et al for this (in Python 3, pass the resulting iterator to list). Could be highly wasteful space-wise though.
  • Iterate over mydict as usual, saving the keys to delete in a seperate collection to_delete. When you’re done iterating mydict, delete all items in to_delete from mydict. Saves some (depending on how many keys are deleted and how many stay) space over the first approach, but also requires a few more lines.

回答 3

而是遍历一个副本,例如items()

for k, v in list(mydict.items()):

Iterate over a copy instead, such as the one returned by items():

for k, v in list(mydict.items()):

回答 4

使用起来最干净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}

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

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

两者都在python2和python3中工作。

It’s cleanest to use 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}

This corresponds to a parallel structure for lists:

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

Both work in python2 and python3.


回答 5

您可以使用字典理解。

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

You can use a dictionary comprehension.

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


回答 6

使用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 )
# {}

With python3, iterate on dic.keys() will raise the dictionary size error. You can use this alternative way:

Tested with python3, it works fine and the Error “dictionary changed size during iteration” is not raised:

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 )
# {}

回答 7

您可以先构建要删除的键列表,然后遍历该列表以删除它们。

dict = {'one' : 1, 'two' : 2, 'three' : 3, 'four' : 4}
delete = []
for k,v in dict.items():
    if v%2 == 1:
        delete.append(k)
for i in delete:
    del dict[i]

You could first build a list of keys to delete, and then iterate over that list deleting them.

dict = {'one' : 1, 'two' : 2, 'three' : 3, 'four' : 4}
delete = []
for k,v in dict.items():
    if v%2 == 1:
        delete.append(k)
for i in delete:
    del dict[i]

回答 8

如果您要删除的项目始终位于dict迭代的“开始”,则有一种方法可能合适

while mydict:
    key, value = next(iter(mydict.items()))
    if should_delete(key, value):
       del mydict[key]
    else:
       break

仅保证“开始”对于某些Python版本/实现是一致的。例如,Python 3.7新增功能

dict对象的插入顺序保留性质已声明是Python语言规范的正式组成部分。

这种方式避免了很多其他答案所暗示的dict副本,至少在Python 3中如此。

There is a way that may be suitable if the items you want to delete are always at the “beginning” of the dict iteration

while mydict:
    key, value = next(iter(mydict.items()))
    if should_delete(key, value):
       del mydict[key]
    else:
       break

The “beginning” is only guaranteed to be consistent for certain Python versions/implementations. For example from What’s New In Python 3.7

the insertion-order preservation nature of dict objects has been declared to be an official part of the Python language spec.

This way avoids a copy of the dict that a lot of the other answers suggest, at least in Python 3.


回答 9

我在Python3中尝试了上述解决方案,但在将对象存储在dict中时,似乎这是唯一对我有用的解决方案。基本上,您会复制dict()并对其进行迭代,同时删除原始词典中的条目。

        tmpDict = realDict.copy()
        for key, value in tmpDict.items():
            if value:
                del(realDict[key])

I tried the above solutions in Python3 but this one seems to be the only one working for me when storing objects in a dict. Basically you make a copy of your dict() and iterate over that while deleting the entries in your original dictionary.

        tmpDict = realDict.copy()
        for key, value in tmpDict.items():
            if value:
                del(realDict[key])