安全地从字典中删除多个键

dub*_*ech 103 python dictionary

我知道要从我的字典中删除一个条目"key" d,安全地,你这样做:

if d.has_key('key'):
    del d['key']
Run Code Online (Sandbox Code Playgroud)

但是,我需要安全地从字典中删除多个条目.我正在考虑在元组中定义条目,因为我需要不止一次地执行此操作.

entitiesToREmove = ('a', 'b', 'c')
for x in entitiesToRemove:
    if d.has_key(x):
        del d[x]
Run Code Online (Sandbox Code Playgroud)

但是,我想知道是否有更聪明的方法来做到这一点?

mat*_*ski 205

d = {'some':'data'}
entriesToRemove = ('any', 'iterable')
for k in entriesToRemove:
    d.pop(k, None)
Run Code Online (Sandbox Code Playgroud)

  • **这.**这是聪明的Pythonista的选择.`dict.pop()`消除了对密钥存在测试的需要._优秀._ (31认同)
  • 对于它的价值,我认为`.pop()`是不好的并且是非Python的,并且相对于此,宁愿接受公认的答案。 (3认同)
  • 数量惊人的人似乎对此并不感到厌烦:)我不介意亲自检查是否存在多余的一行,除非您已经了解pop(),否则它的可读性会更高。另一方面,如果您尝试通过理解或内联lambda来执行此操作,则此技巧可能会大有帮助。我还要说,在我看来,结识当地人很重要。我不确定“差劲而令人生厌”会给正在阅读这些答案的人们所寻找的实际指导。 (3认同)
  • 使用它有一个特别好的理由.虽然添加额外的行可以提高"可读性"或"清晰度",但它还为字典添加了额外的查找.这种方法相当于删除`setdefault`.如果正确实现(并且我确定它是),它只会查找一个哈希映射,即`dict`,而不是两个. (3认同)
  • 这并不是“糟糕且不符合Python的”。`pop` 非常常见,也非常 Pythonic。或者,您也可以尝试/排除,但这就是 pop 中已经内置的内容。继续。 (3认同)
  • 提醒 - 如果您正在测试此功能并且仅使用一个键,则需要执行以下操作:entriesToRemove('any',)。(末尾有额外的“,”)。如果您忘记了逗号,它将改为测试每个字母。 (3认同)
  • 我个人将首先关注正确性和可维护性,只有在证明速度不够快的情况下才关注速度。当缩小到应用程序级别时,这些操作之间的速度差异将变得微不足道。可能是这样的情况下速度更快,但是我希望在现实世界中使用时,您既不会注意到也不会在意,如果您确实注意到并在意,则最好用比Python更高性能的方式重写。 (2认同)

Abh*_*ogi 79

使用Dict理解

final_dict = {key: t[key] for key in t if key not in [key1, key2]}
Run Code Online (Sandbox Code Playgroud)

其中key1key2将被删除.

在下面的示例中,键"b"和"c"将被删除,并保存在键列表中.

>>> a
{'a': 1, 'c': 3, 'b': 2, 'd': 4}
>>> keys = ["b", "c"]
>>> print {key: a[key] for key in a if key not in keys}
{'a': 1, 'd': 4}
>>> 
Run Code Online (Sandbox Code Playgroud)

  • 为了便于阅读,我建议{k:v代表k,v代表t.items()如果k不在[key1,key2]} (10认同)
  • 当键列表太大时,这也会出现性能问题,因为搜索需要"O(n)".整个操作是`O(mn)`,其中`m`是dict中键的数量,`n`是列表中键的数量.我建议使用一组`{key1,key2}`,如果可能的话. (7认同)
  • 当持有的变量在程序中进一步使用时,该解决方案具有严重的性能损失.换句话说,删除键的dict比使用保留项的新创建的dict更有效. (4认同)
  • 新字典?清单理解力?您应该根据提出问题的人来调整答案;) (3认同)
  • @shadyabhi。美丽,非常pythonic!人们常常忘记优化和副作用是邪恶的。 (2认同)
  • 致阿帕拉拉(Apalala):您能帮助我理解为什么会有效果受到打击吗? (2认同)
  • @Sean 该程序需要为第二个字典分配内存,遍历第一个字典上的每个键并进行全值比较(而不是哈希比较),通过迭代检查它是否在“键”列表中列出理解的每一遍,如果没有,则复制到第二个字典。通过删除,所发生的只是第一个字典查找键的哈希值并将其删除。然后垃圾收集器会处理剩下的事情。因此,这要快得多。 (2认同)

Gla*_*los 42

为什么不这样:

entries = ('a', 'b', 'c')
the_dict = {'b': 'foo'}

def entries_to_remove(entries, the_dict):
    for key in entries:
        if key in the_dict:
            del the_dict[key]
Run Code Online (Sandbox Code Playgroud)

mattbornski使用dict.pop()提供了更紧凑的版本

  • 为来自搜索引擎的人添加此项.如果知道密钥(当安全性不成问题时),可以在一行中删除多个密钥,如`del dict ['key1'],dict ['key2'],dict ['key3']` (8认同)
  • 根据您要删除的键的数量,使用“for key in set(the_dict) & Entry:”并绕过“key in dict”测试可能会更有效。 (3认同)

Jos*_* M. 20

解决方案是使用mapfilter功能

python 2

d={"a":1,"b":2,"c":3}
l=("a","b","d")
map(d.__delitem__, filter(d.__contains__,l))
print(d)
Run Code Online (Sandbox Code Playgroud)

蟒蛇3

d={"a":1,"b":2,"c":3}
l=("a","b","d")
list(map(d.__delitem__, filter(d.__contains__,l)))
print(d)
Run Code Online (Sandbox Code Playgroud)

你得到:

{'c': 3}
Run Code Online (Sandbox Code Playgroud)

  • 或者`deque(map(...),maxlen = 0)`以避免构建None值列表; 首先使用`from collections import deque`导入 (3认同)

And*_*ark 18

如果您还需要检索要删除的键的值,这将是一个非常好的方法:

valuesRemoved = [d.pop(k, None) for k in entitiesToRemove]
Run Code Online (Sandbox Code Playgroud)

您当然可以仅仅为了删除密钥d而执行此操作,但是您将不必要地使用列表推导创建值列表.仅仅为了函数的副作用使用列表理解也有点不清楚.

  • 或者,如果您想将已删除的条目*保留为字典:*`valuesRemoved = dict((k,d.pop(k,None))for entitiesToRemove中的k)等等. (3认同)

Shu*_*ava 13

找到了一个解决方案popmap

d = {'a': 'valueA', 'b': 'valueB', 'c': 'valueC', 'd': 'valueD'}
keys = ['a', 'b', 'c']
list(map(d.pop, keys))
print(d)
Run Code Online (Sandbox Code Playgroud)

这个的输出:

{'d': 'valueD'}
Run Code Online (Sandbox Code Playgroud)

我这么晚才回答这个问题,只是因为我认为如果有人搜索相同的内容,将来会有所帮助。这可能会有所帮助。

更新

如果字典中不存在键,则上述代码将引发错误。

DICTIONARY = {'a': 'valueA', 'b': 'valueB', 'c': 'valueC', 'd': 'valueD'}
keys = ['a', 'l', 'c']

def remove_keys(key):
    try:
        DICTIONARY.pop(key, None)
    except:
        pass  # or do any action

list(map(remove_key, keys))
print(DICTIONARY)
Run Code Online (Sandbox Code Playgroud)

输出:

DICTIONARY = {'b': 'valueB', 'd': 'valueD'}
Run Code Online (Sandbox Code Playgroud)


kol*_*pto 8

我测试了三种方法的性能:

# Method 1: `del`
for key in remove_keys:
    if key in d:
        del d[key]

# Method 2: `pop()`
for key in remove_keys:
    d.pop(key, None)

# Method 3: comprehension
{key: v for key, v in d.items() if key not in remove_keys}
Run Code Online (Sandbox Code Playgroud)

以下是 1M 次迭代的结果:

  1. del:2.03s 2.0 ns/iter (100%)
  2. pop():2.38s 2.4 ns/iter (117%)
  3. 理解:4.11s 4.1 ns/iter (202%)

所以 和del都是pop()最快的。理解速度慢 2 倍。但无论如何,我们在这里说的是纳秒:) Python 中的字典速度快得离谱。


Eri*_*sty 7

cpython 3 的一些计时测试表明,简单的 for 循环是最快的方法,并且非常易读。添加一个函数也不会造成太多开销:

timeit 结果(10k 次迭代):

  • all(x.pop(v) for v in r) # 0.85
  • all(map(x.pop, r)) # 0.60
  • list(map(x.pop, r)) # 0.70
  • all(map(x.__delitem__, r)) # 0.44
  • del_all(x, r) # 0.40
  • <inline for loop>(x, r) # 0.35
def del_all(mapping, to_remove):
      """Remove list of elements from mapping."""
      for key in to_remove:
          del mapping[key]
Run Code Online (Sandbox Code Playgroud)

对于小迭代,由于函数调用的开销,执行“内联”要快一些。但是del_alllint 安全、可重用,并且比所有 python 理解和映射构造更快。


Dou*_* R. 5

我对任何现有答案都没有问题,但我很惊讶没有找到这个解决方案:

keys_to_remove = ['a', 'b', 'c']
my_dict = {k: v for k, v in zip("a b c d e f g".split(' '), [0, 1, 2, 3, 4, 5, 6])}

for k in keys_to_remove:
    try:
        del my_dict[k]
    except KeyError:
        pass

assert my_dict == {'d': 3, 'e': 4, 'f': 5, 'g': 6}
Run Code Online (Sandbox Code Playgroud)

注意:我从这里偶然发现了这个问题。我的答案与这个答案有关