我如何从字典中删除项目,而迭代它?

在Python中,我可以在遍历字典时删除字典中的项吗?

我想从字典中删除不符合特定条件的元素,而不是创建一个全新的字典。以下是一个好的解决方案,还是有更好的方法?

for k, v in mydict.items():
if k == val:
del mydict[k]
244488 次浏览

相反,迭代一个副本,例如items()返回的副本:

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

在迭代集合时不能修改集合。这种方式是疯狂的——最明显的是,如果你被允许删除和删除当前项,迭代器将不得不移动(+1),而下一次调用next将超出(+2),所以你最终会跳过一个元素(在你删除的元素后面的那个元素)。你有两个选择:

  • 复制所有键(或值,或两者都复制,这取决于您需要什么),然后遍历这些键。为此,可以使用.keys()等方法(在Python 3中,将结果迭代器传递给list)。不过,这可能是非常浪费空间的。
  • 像往常一样遍历mydict,将要删除的键保存在单独的集合to_delete中。当你完成迭代mydict时,从mydict中删除to_delete中的所有项。与第一种方法相比节省了一些空间(取决于删除了多少个键和保留了多少个键),但也需要多几行。

Python 3 +:

>>> mydict
{'four': 4, 'three': 3, 'one': 1}


>>> for k in list(mydict.keys()):
...     if mydict[k] == 3:
...         del mydict[k]


>>> mydict
{'four': 4, 'one': 1}

其他答案适用于Python 2,但会对Python 3抛出RuntimeError:

RuntimeError:在迭代过程中字典改变了大小。

这是因为mydict.keys()返回的是一个迭代器而不是一个列表。 正如评论中指出的那样,简单地将mydict.keys()转换为list(mydict.keys())的列表,它应该可以工作

Python 2:

在控制台中进行的一个简单测试显示,在遍历字典时不能修改它:

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

如果你需要根据item的值进行删除,请使用items()方法:

>>> for k, v in mydict.items():
...     if v == 3:
...         del mydict[k]


>>> mydict
{'four': 4, 'one': 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)

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

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

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]

你可以使用字典理解法。

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

使用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中工作。

我在Python3中尝试了上述解决方案,但在字典中存储对象时,这一个似乎是唯一适合我的解决方案。基本上,您可以创建dict()的副本,并在删除原始字典中的条目的同时遍历该副本。

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

如果想要删除的项总是在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语言规范的正式组成部分。

这种方法避免了许多其他答案所建议的字典副本,至少在Python 3中是这样。

一行程序:

my_dict = {k: v for k, v in my_dict.copy().items() if not k == value}

my_dict.copy()对象仅用于迭代,在字典理解范围之外不可用。这避免了编辑当前迭代的对象,正如@user395760所建议的那样。

为了清晰起见,你可以把它分割成多行:

my_dict = {
k: v
for k, v in my_dict.copy().items()
if not k == value
}