关于脚本:如何在迭代字典时从字典中删除项目?

How to delete items from a dictionary while iterating over it?

在python中迭代时从字典中删除项目是否合法?

例如:

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

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

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


编辑:

这个答案不适用于python3,会给出一个RuntimeError

RuntimeError: dictionary changed size during iteration.

这是因为mydict.keys()返回迭代器而不是列表。正如评论中指出的,只需将mydict.keys()转换成list(mydict.keys())的列表,它就可以工作了。

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

1
2
3
4
5
6
7
8
9
>>> 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()方法获取密钥列表并使用该列表:

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

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

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


您还可以分两步完成:

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

我最喜欢的方法通常是做一个新的口述:

1
2
3
4
# 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)


迭代集合时不能修改它。这样做有点疯狂——最明显的是,如果允许您删除和删除当前项,迭代器将不得不继续(+1),下一次调用next将使您超过这个范围(+2),因此您最终将跳过一个元素(刚好在您删除的元素之后)。您有两种选择:

  • 复制所有键(或者值,或者两者都复制,这取决于您需要什么),然后迭代这些键。您可以使用.keys()等(在python 3中,将生成的迭代器传递给list)。不过,这可能是高度浪费空间的明智之举。
  • 照常在mydict上迭代,将要删除的键保存在单独的集合to_delete中。迭代mydict后,从mydict中删除to_delete中的所有项。在第一个方法中节省一些(取决于删除了多少个键以及保留了多少)空间,但也需要更多的行。


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

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


使用python3,迭代dic.keys()将导致字典大小错误。您可以使用以下替代方法:

使用python3进行测试,它工作正常,不会出现"Dictionary changed size during iteration"(字典在迭代期间更改大小)错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
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 )
#&nbsp;{}

当我从一个使用大量内存的字典中,想要构建另一个字典(包含对第一个字典的修改)而不执行"复制"和重载RAM时,我就使用它。


使用list(mydict)最干净:

1
2
3
4
5
6
7
>>> 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}

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

1
2
3
4
5
6
7
>>> mylist = ['one', 'two', 'three', 'four']
>>> for k in list(mylist):                            # or mylist[:]
...     if k == 'three':
...         mylist.remove(k)
...
>>> mylist
['one', 'two', 'four']

这两种方法都适用于python2和python3。


你可以用字典来理解。

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


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

1
2
3
4
5
6
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]


我在python3中尝试了以上的解决方案,但在将对象存储在dict中时,这个解决方案似乎是唯一对我有用的解决方案。基本上,您可以复制dict(),并在删除原始字典中的条目的同时重复该解决方案。

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