使用for循环从字典中删除项
我想从一个字典中删除一些项目,条件是这些项目的值低于某个特定的阈值。举个简单的例子:
my_dict = {'blue': 1, 'red': 2, 'yellow': 3, 'green': 4}
for color in my_dict:
threshold_value = 3
if my_dict[color] < threshold_value:
del my_dict[color]
print(my_dict)
现在,我遇到了一个错误:RuntimeError: dictionary changed size during iteration
。这并不令人意外。我发这个问题的原因是:
想看看有没有优雅的解决方案,不需要创建一个新的字典(只包含值大于等于阈值的键)。
试着理解一下Python在这里的想法。我理解的方式是:“去第一个键。这个键的值是否小于x?如果是,就删除这个键值对,然后继续下一个键;如果不是,就不做任何事情,继续下一个键。”换句话说,之前的键发生了什么不应该影响我接下来要去哪里。我只关注接下来的项目,而不管过去的情况。我知道这个想法有点奇怪(有些人可能会说傻,我承认),但Python在这个循环中的“思维方式”是什么?为什么它不工作?如果Python自己读出来,会怎么说?我只是想更好地理解这个语言……
3 个回答
我想说,在遍历一个集合的时候去修改它是件很难做到的事情。看看下面这个例子:
>>> list = [1, 2, 3, 4, 5, 6]
>>> for ii in range(len(list)):
print list[ii];
if list[ii] == 3:
del list[ii]
1
2
3
5
6
注意,在这个例子中,数字4完全被省略了。这在字典中也是类似的,删除或添加条目可能会破坏内部结构,这些结构决定了遍历的顺序(比如你删除了足够多的条目,导致哈希表的桶大小发生了变化)。
要解决你的问题——只需创建一个新的字典,并把条目复制过去就可以了。
字典是无序的。这意味着如果你删除了一个键,没人能确定下一个键是什么。所以在Python中,一般不允许在遍历字典的时候添加或删除键。
如果需要改变字典,最简单的方法就是创建一个新的字典:
my_dict = {"blue":1,"red":2,"yellow":3,"green":4}
new_dict = {k:v for k,v in my_dict.iteritems() if v >= threshold_value}
因为Python的字典是用哈希表实现的,所以你不能指望它们有任何顺序。键的顺序可能会在你插入或删除键后发生不可预测的变化。因此,你无法预测下一个键是什么。为了安全起见,Python会抛出一个RuntimeError
,以防止人们遇到意想不到的结果。
在Python 2中,dict.items
方法会返回键值对的一个副本,这样你可以安全地遍历它,并通过键删除不需要的值,就像@wim在评论中建议的那样。示例:
for k, v in my_dict.items():
if v < threshold_value:
del my_dict[k]
然而,在Python 3中,dict.items
返回的是一个视图对象,它会反映字典所做的所有更改。这就是为什么上面的解决方案只在Python 2中有效的原因。你可以将my_dict.items()
转换为list
(tuple
等),以使其兼容Python 3。
另一种解决问题的方法是先选择你想删除的键,然后再删除它们。
keys = [k for k, v in my_dict.items() if v < threshold_value]
for x in keys:
del my_dict[x]
这种方法在Python 2和Python 3中都有效。