当字典中的键未知时,从字典中删除一个项目。

116

如何通过值从字典中删除一个项目,即当该项目的键未知时,最好的方法是什么?以下是一种简单的方法:

for key, item in some_dict.items():
    if item is item_to_remove:
        del some_dict[key]

有更好的方法吗?在迭代字典时变异(删除项)是否存在问题?


2
禁止在迭代字典时进行修改的根本原因是因为内部存在迭代顺序,如果您更改键,则顺序将被破坏,从而导致未知行为。 - user2558887
可能是如何从Python字典中删除一个键?的重复问题。 - tripleee
10个回答

118
dict.pop(key[, default])方法允许您在已知键的情况下删除项。如果它删除了该项,则返回键处的值,否则返回传递的default值。请参阅文档

示例:

>>> dic = {'a':1, 'b':2}
>>> dic
{'a': 1, 'b': 2}
>>> dic.pop('c', 0)
0
>>> dic.pop('a', 0)
1
>>> dic
{'b': 2}

7
OP 询问当密钥未知时的情况。 - nmz787

93

请注意您现在正在测试对象的身份(is仅在两个操作数在内存中由同一对象表示时返回True - 但这并不总是与使用==比较相等的两个对象的情况相同)。 如果您是有意为之,请将代码重写为

some_dict = {key: value for key, value in some_dict.items() 
             if value is not value_to_remove}

但是这可能不会达到你的预期:

>>> some_dict = {1: "Hello", 2: "Goodbye", 3: "You say yes", 4: "I say no"}
>>> value_to_remove = "You say yes"
>>> some_dict = {key: value for key, value in some_dict.items() if value is not value_to_remove}
>>> some_dict
{1: 'Hello', 2: 'Goodbye', 3: 'You say yes', 4: 'I say no'}
>>> some_dict = {key: value for key, value in some_dict.items() if value != value_to_remove}
>>> some_dict
{1: 'Hello', 2: 'Goodbye', 4: 'I say no'}

你可能想要使用!=而不是is not


2
那是字典压缩吗?它们是什么时候加入的? - Buttons840
4
你可以在这里使用 some_dict.iteritems(),并将forif 语句分开成不同的行以提高可读性。 - jfs
3
我相信Python 2.7中增加了字典推导式。 - mithrandi
2
@J.F. Sebastian: 我使用的是 Python 3,然后iteritems现在要写成 items。在 Python 2.7 中,确实iteritems()更好用。 - Tim Pietzcker
1
@Buttons840 它们在 PEP 274 中被称为“字典推导式”或“字典显示”。正如 PEP 所说,它们是在 2.7 版本中添加的,作为后移的 3.x 特性。或者,您可以使用适当的生成器表达式来提供 dict(),这是 2.4 版本的功能。元数据:可以在此处浏览 peps 以查找相关信息:http://legacy.python.org/dev/peps/。 - n611x007

52
a = {'name': 'your_name','class': 4}
if 'name' in a: del a['name']

OP 询问当密钥未知时的情况。本答案假定密钥已知。 - Jean-François Corbett

42

delpop() 的简单比较:

import timeit
code = """
results = {'A': 1, 'B': 2, 'C': 3}
del results['A']
del results['B']
"""
print timeit.timeit(code, number=100000)
code = """
results = {'A': 1, 'B': 2, 'C': 3}
results.pop('A')
results.pop('B')
"""
print timeit.timeit(code, number=100000)

结果:

0.0329667857143
0.0451040902256
因此,delpop()更快。

6
但是,两种方法的性能差异并不大,如果您想避免引发异常,可以向 pop() 方法提供第二个参数(与 @n-1-1 上面所做的一样),但这对于 del 运算符来说不是一个选项。 - Alex Dupuy
1
除了问题本身,我也一直在努力理解timeit。感谢您提供的这个清晰的例子。 - Adam_G
OP 询问了关于当密钥未知时的情况。本回答假设密钥已知。 - Jean-François Corbett

8

我会建立一个需要删除的键列表,然后将它们删除。这样简单高效,并避免了同时迭代和改变字典时可能出现的问题。

keys_to_remove = [key for key, value in some_dict.iteritems()
                  if value == value_to_remove]
for key in keys_to_remove:
    del some_dict[key]

OP 询问了关于当密钥未知时的情况。本回答假设密钥已知。 - Jean-François Corbett

7

items() 返回一个列表,你正在遍历这个列表,所以在循环中改变字典并不重要。如果你使用的是 iteritems(),在循环中改变字典 会有问题,Python 2.7 中的 viewitems() 同理。

我想不出更好的通过值从字典中删除条目的方法。


2

1
y={'username':'admin','machine':['a','b','c']}
if 'c' in y['machine'] : del y['machine'][y['machine'].index('c')]

0

这是我会做的方法。

for key in some_dict.keys():
    if some_dict[key] == item_to_remove:
        some_dict.pop(key)
        break

0

for k,v in d.iteritems(): del d[k] 会出现 RuntimeError: dictionary changed size during iteration 错误。请参考 mithrandi 的解释。 - Buttons840
1
当然,d.iteritems() 不是原帖中迭代的方式,也不是我在回答中所指的。 - Thane Anthem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接