使用for循环从字典中删除项

9 投票
3 回答
12159 浏览
提问于 2025-04-18 06:12

我想从一个字典中删除一些项目,条件是这些项目的值低于某个特定的阈值。举个简单的例子:

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。这并不令人意外。我发这个问题的原因是:

  1. 想看看有没有优雅的解决方案,不需要创建一个新的字典(只包含值大于等于阈值的键)。

  2. 试着理解一下Python在这里的想法。我理解的方式是:“去第一个键。这个键的值是否小于x?如果是,就删除这个键值对,然后继续下一个键;如果不是,就不做任何事情,继续下一个键。”换句话说,之前的键发生了什么不应该影响我接下来要去哪里。我只关注接下来的项目,而不管过去的情况。我知道这个想法有点奇怪(有些人可能会说傻,我承认),但Python在这个循环中的“思维方式”是什么?为什么它不工作?如果Python自己读出来,会怎么说?我只是想更好地理解这个语言……

3 个回答

0

我想说,在遍历一个集合的时候去修改它是件很难做到的事情。看看下面这个例子:

>>> 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完全被省略了。这在字典中也是类似的,删除或添加条目可能会破坏内部结构,这些结构决定了遍历的顺序(比如你删除了足够多的条目,导致哈希表的桶大小发生了变化)。

要解决你的问题——只需创建一个新的字典,并把条目复制过去就可以了。

2

字典是无序的。这意味着如果你删除了一个键,没人能确定下一个键是什么。所以在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}
15

因为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()转换为listtuple等),以使其兼容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中都有效。

撰写回答