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


127

我知道d安全地从字典中删除条目“键” ,您可以这样做:

if d.has_key('key'):
    del d['key']

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

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

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


3
由于散列,从字典中检索时间接近O(1)。除非您要删除大部分条目,否则我认为您不会做得更好。
ncmathsadist,2012年

1
@mattbornski的答案似乎更规范,也很简洁。
Ioannis Filippidis 2015年

2
所说的StackOverflow:key in dd.has_key(key) stackoverflow.com/questions/1323410/has-key-or-in
Michael Scheper

如果可以节省一些内存,则可以这样做for x in set(d) & entities_to_remove: del d[x]。如果entities_to_remove为“大”,这可能只会更有效率。
DylanYoung

Answers:


56

为什么不这样:

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]

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


14
为来自搜索引擎的用户添加此功能。如果知道密钥(不是安全问题),可以在一行中删除多个密钥del dict['key1'], dict['key2'], dict['key3']
Tirtha R

根据要删除的键的数量,使用for key in set(the_dict) & entries:和绕过key in dict测试可能更有效。
DylanYoung

235
d = {'some':'data'}
entriesToRemove = ('any', 'iterable')
for k in entriesToRemove:
    d.pop(k, None)

37
这个。这是聪明的Pythonista的选择。dict.pop()无需进行密钥存在性测试。优秀的。
塞西尔·库里

4
对于它的价值,我认为这.pop()是不好的,而且不切实际,并且相对于此,我更愿意接受公认的答案。
Arne

5
数量惊人的人似乎对此并不感到厌烦:)我不介意亲自检查是否存在多余的一行,除非您已经了解pop(),否则它的可读性要高得多。另一方面,如果您尝试通过理解或内联lambda来执行此操作,则此技巧可能会大有帮助。我还要说,在我看来,结识当地人很重要。我不确定“差劲和令人生厌”会给正在阅读这些答案的人们所寻找的实际指导。
mattbornski '18

5
有一个特别好的理由来使用它。虽然添加额外的行可能会提高“可读性”或“清晰度”,但同时也会增加字典的查找范围。此方法等效于doing setdefault。如果正确实施(我确定是正确实​​施),则它只会对哈希映射(即)进行一次查找dict,而不是两次。
疯狂物理学家

2
我个人将首先关注正确性和可维护性,只有在证明速度不够快的情况下才关注速度。当缩小到应用程序级别时,这些操作之间的速度差异将变得微不足道。可能是这样的情况下,速度更快,但是我希望在现实世界中使用时,您既不会注意到也不会在意,如果您确实注意到并在意,则最好使用比Python更高性能的方式重写。
mattbornski

89

使用词典理解

final_dict = {key: t[key] for key in t if key not in [key1, key2]}

其中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}
>>> 

4
新字典?清单理解力?您应该调整提问者的答案;)
Glaslos 2012年

6
当包含变量的变量在程序中进一步使用时,此解决方案会严重影响性能。换句话说,从中删除键的字典比带有保留项的新创建的字典要有效得多。
阿帕拉拉

14
出于可读性考虑,我建议{如果k不位于[key1,key2]中,则在t.items()中使用k,v的k:v}
Frederic Bazin 2015年

8
当键列表太大时(如搜索),这也会带来性能问题O(n)。整个操作过程O(mn),其中m dict中n的键数和列表中的键数为。{key1, key2}如果可能的话,我建议使用一组。
ldavid

4
致阿帕拉拉(Apalala):您能帮助我理解为什么会有效果受到打击吗?
肖恩

21

解决方案正在使用mapfilter起作用

蟒蛇2

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

蟒蛇3

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

你得到:

{'c': 3}

对于Python 3.4,这对我不起作用:>>> d={"a":1,"b":2,"c":3} >>> l=("a","b","d") >>> map(d.__delitem__, filter(d.__contains__,l)) <map object at 0x10579b9e8> >>> print(d) {'a': 1, 'b': 2, 'c': 3}
Risadinha 2015年

@Risadinha list(map(d.__delitem__,filter(d.__contains__,l)))....在python 3.4中,map函数返回一个迭代器
Jose Ricardo Bustos M.

4
deque(map(...), maxlen=0)避免建立None值列表;第一次进口from collections import deque
Jason

19

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

valuesRemoved = [d.pop(k, None) for k in entitiesToRemove]

当然,您仍然可以仅从中删除键来执行此操作d,但是您将不必要使用列表理解来创建值列表。只是为了函数的副作用而使用列表理解也有点不清楚。


3
或者,如果您想将删除的条目保留为字典: valuesRemoved = dict((k, d.pop(k, None)) for k in entitiesToRemove)等等。
kindall 2012年

您可以省略对变量的分配。以这种方式,这是最短和最pythonic的解决方案,应将其标记为核心答案恕我直言。
Gerhard Hagerer,2015年

12

发现用溶液popmap

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

此输出:

{'d': 'valueD'}

我这么晚才回答了这个问题,只是因为我认为如果有人进行搜索,将来会有所帮助。这可能会有所帮助。

更新资料

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

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)

输出:

DICTIONARY = {'b': 'valueB', 'd': 'valueD'}

1
如果任何键入keys都不存在,此答案将引发异常d-您必须首先过滤该异常。
ingofreyer

@ingofreyer更新了用于异常处理的代码。感谢您发现此问题。我认为现在它将起作用。:)
Shubham Srivastava

谢谢,这应该可以帮助所有人找到此答案:-)
ingofreyer

创建列表作为使用map的副产品,这会很慢,实际上最好循环遍历。
查理·克拉克

4

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

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}

注:我碰到这个问题,从跌跌撞撞来这里。我的答案与此答案有关


3

为什么不:

entriestoremove = (2,5,1)
for e in entriestoremove:
    if d.has_key(e):
        del d[e]

我不知道您所说的“更聪明的方式”。当然,还有其他方法,也许是对字典的理解:

entriestoremove = (2,5,1)
newdict = {x for x in d if x not in entriestoremove}

2

排队

import functools

#: not key(c) in d
d = {"a": "avalue", "b": "bvalue", "d": "dvalue"}

entitiesToREmove = ('a', 'b', 'c')

#: python2
map(lambda x: functools.partial(d.pop, x, None)(), entitiesToREmove)

#: python3

list(map(lambda x: functools.partial(d.pop, x, None)(), entitiesToREmove))

print(d)
# output: {'d': 'dvalue'}

2

对cpython 3的一些计时测试表明,简单的for循环是最快的方法,并且可读性强。添加一个函数也不会导致太多开销:

timeit结果(10000次迭代):

  • 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]

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


0

我认为,如果您使用的是python 3,最好将键视为一个集合:

def remove_keys(d, keys):
    to_remove = set(keys)
    filtered_keys = d.keys() - to_remove
    filtered_values = map(d.get, filtered_keys)
    return dict(zip(filtered_keys, filtered_values))

例:

>>> remove_keys({'k1': 1, 'k3': 3}, ['k1', 'k2'])
{'k3': 3}

0

完全支持字典的set方法(而不是我们在Python 3.9中遇到的麻烦)是很好的,这样您就可以简单地“删除”一组键。但是,只要不是这种情况,并且您有一个大型词典并且可能要删除大量键,则可能需要了解性能。因此,我创建了一些代码,该代码创建的大小足以进行有意义的比较:100,000 x 1000矩阵,因此总共10,000,00个项目。

from itertools import product
from time import perf_counter

# make a complete worksheet 100000 * 1000
start = perf_counter()
prod = product(range(1, 100000), range(1, 1000))
cells = {(x,y):x for x,y in prod}
print(len(cells))

print(f"Create time {perf_counter()-start:.2f}s")
clock = perf_counter()
# remove everything above row 50,000

keys = product(range(50000, 100000), range(1, 100))

# for x,y in keys:
#     del cells[x, y]

for n in map(cells.pop, keys):
    pass

print(len(cells))
stop = perf_counter()
print(f"Removal time {stop-clock:.2f}s")

在某些情况下,1000万个或更多的项目并不罕见。比较本地计算机上的这两种方法,我发现使用map和时会略有改善pop,大概是因为调用的函数较少,但是这两种方法在我的计算机上大约需要2.5秒的时间。但这与首先创建字典(55s)或在循环中包括检查所需的时间相比显得苍白。如果可能,那么最好创建一个集合,该集合是字典键和过滤器的交集:

keys = cells.keys() & keys

总结:del已经进行了优化,所以不用担心使用它。


-1

我迟到了这个讨论,但对于其他人。解决方案可以是这样创建键列表。

k = ['a','b','c','d']

然后在列表推导或for循环中使用pop()遍历这些键,并一次弹出一个键。

new_dictionary = [dictionary.pop(x, 'n/a') for x in k]

如果密钥不存在,则“ n / a”,则需要返回默认值。


8
new_dictionary看起来很像列表;)
DylanYoung
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.