从字典中删除元素


1390

有没有办法从Python的字典中删除项目?

另外,如何从字典中删除项目以返回副本(即不修改原始内容)?


13
当您可以直接修改字典时,为什么需要一个返回字典的函数?
腹泻2011年

5
词典pop的方法改变了字典原地。因此,它将更改对从调用方传递到“帮助功能”的字典的引用。因此,“帮助器函数”不需要返回任何内容,因为调用者中对字典的原始引用已经被更改。dict.pop()如果不需要,不要将收益分配给任何东西。EG :do stuff with my_dict; my_dict.pop(my_key, None); do more stuff with my_dict # now doesn't have my_keydeepcopy(my_dict)必要时使用。
Mark Mikofski '16

1
由于原始标题不同意细节,特别是排除了明显的解决方案d.pop(),因此我修正了标题以询问细节中指定的问题。
smci

1
我们应该添加一个警告,询问您是否真的要执行此操作,就像在具有E个元素的字典上执行N次一样,您将使用所有深层副本泄漏(/使用)O(N * E)内存。如果只需要只读(浅副本),请执行d.pop(key)。但是,如果有任何修改过浅表副本的操作,则别名将存在一个众所周知的问题。如果您告诉我们更广泛的内容,将会有所帮助。(是否还有其他修改dict值的内容?您是否要破坏性地遍历列表?如果没有,那是什么?)
smci

5
“当您直接修改字典时,为什么需要一个返回字典的函数?” 也许是因为您要编写修改其参数的纯函数?
Gene Callahan

Answers:


1724

del语句删除一个元素:

del d[key]

但是,这会使现有字典发生变化,因此对于引用同一实例的其他任何人,字典的内容都会更改。要返回词典,请复制该词典:

def removekey(d, key):
    r = dict(d)
    del r[key]
    return r

dict()构造使得浅拷贝。要进行深拷贝,请参阅copy模块


请注意,为每个字典del/ assignment / etc 复制一份。意味着您要从恒定时间变为线性时间,并且还要使用线性空间。对于小命令,这不是问题。但是,如果您打算复制大量大型字典,则可能需要不同的数据结构,例如HAMT(如本答案所述)。


15
多数民众赞成在字典+1的可变性方面很重要-尽管我想不起来想要字典副本的时候,但我一直都依赖于“ everyones”副本相同。好点。
tMC 2011年

30
@tMC如果您dict在遍历时对其进行编辑,则会给您一个错误:RuntimeError: dictionary changed size during iteration
VertigoRay

15
什么pop方法,其实不一样吗?难道不是pythonic吗?(是字典的方法,不是特殊保留字)?
Serge 2014年

21
这个答案有一个弱点,可能会引起误解。读者可能会误解dict(d)可以给他们一个带有“ d”的副本。但这是不完整的副本。仅执行del键操作时,就可以了。但是,当您想对嵌套字典进行其他操作时,使用该复制方法修改“ r”可能会导致对原始“ d”的更改。要获取真实副本,您首先需要“导入副本”,然后是“ r = copy.deepcopy(d)”。
2014年

10
@Zen:公平地说,我添加了关于浅层副本与深层副本的说明。
格雷格·休吉尔

264

pop 使字典变异。

 >>> lol = {"hello": "gdbye"}
 >>> lol.pop("hello")
     'gdbye'
 >>> lol
     {}

如果您想保留原件,则可以将其复印。


29
我认为“ del”还可以,但“ pop”似乎更像“ Pythonic”。
ivanleoncz

1
@ivanleoncz为什么?
kevr

3
pop返回“弹出”的值,这使您可以出于任何其他原因使用此值。如果不是更“ Pythonic”,那肯定会更好:)。这不是一个命令,但对两者都起作用
ivanleoncz

2
@ivanleoncz出于另一个原因,它也更好,pop可以提供默认值,当dict缺少键时将返回该默认值。当您需要删除一些键但其中一些键可能会丢失时,这很好。在这种情况下del会抛出KeyError
itachi

80

我认为您的解决方案是最好的方法。但是,如果您需要其他解决方案,则可以使用旧字典中的键来创建新字典,而无需包括指定的键,如下所示:

>>> a
{0: 'zero', 1: 'one', 2: 'two', 3: 'three'}
>>> {i:a[i] for i in a if i!=0}
{1: 'one', 2: 'two', 3: 'three'}

2
真的很酷 我喜欢无需定义新功能即可过滤字典的快速方法。
Joe J

6
对于不熟悉理解的人,您也可以执行以下操作:{i:a[i] for i in a if i not in [0, 1, 2]}如果要删除多个元素。
kmatheny

9
{k:v for k,v in a.items() if k != 0}我认为更好。
rlbond

1
通过键删除项并在同一行中返回新字典结果的最佳解决方案。例如,如果您需要使用已经构建好的字典,而没有单个项目,例如**kwargssome_function(**{k:v for k,v in some_dict.items() if k not 'some_key'})
Cole

最好的解决方案。一线,它不会变异原始字典。
安德鲁·温特伯瑟姆'18

55

del语句是你在找什么。如果您有一个名为foo的字典,其键名为“ bar”,则可以从foo中删除“ bar”,如下所示:

del foo['bar']

请注意,这将永久修改正在操作的词典。如果要保留原始词典,则必须事先创建一个副本:

>>> foo = {'bar': 'baz'}
>>> fu = dict(foo)
>>> del foo['bar']
>>> print foo
{}
>>> print fu
{'bar': 'baz'}

dict调用将进行浅表复制。如果要深拷贝,请使用copy.deepcopy

为了方便起见,您可以使用以下方法复制和粘贴:

def minus_key(key, dictionary):
    shallow_copy = dict(dictionary)
    del shallow_copy[key]
    return shallow_copy

1
@ pythonian29033,实际上,。接受的答案按预期方式工作-它返回不带键的字典。从这个答案变异原字典的方法;)有significat差异
maxkoryukov

1
@ arussell84,为什么>>>经常在python-examples中使用?是的,python-doc包含很多这样的东西。但是这样的代码对于复制粘贴并不方便。我很困惑……
maxkoryukov

@maxkoryukov是的!但是该函数与该答案完全相同,但该答案位于函数内部。而且您一定有一段时间没有用python编码,它>>>以cli模式模仿了python的侦听符号
pythonian29033

2
@ pythonian29033关于>>>。是的,它是REPL风格的,但坦率地说:我们只有一个人写了这个样本,有1000人读了这个样本。我认为,以允许轻松复制和运行的方式编写示例非常好。我不喜欢用手移除此尖括号。还是逐行复制..所以我不明白:为什么这个角度仍然存在)))也许我不知道什么?
maxkoryukov

3
为了方便起见,我添加了可以复制/粘贴的功能。
arussell84

48

有很多不错的答案,但我想强调一件事。

您可以同时使用dict.pop()方法和更通用的del语句从字典中删除项目。它们都变异了原始词典,因此您需要进行复制(请参见下面的详细信息)。

KeyError如果您要提供给他们的密钥在词典中不存在,则这两个都将引发一个:

key_to_remove = "c"
d = {"a": 1, "b": 2}
del d[key_to_remove]  # Raises `KeyError: 'c'`

key_to_remove = "c"
d = {"a": 1, "b": 2}
d.pop(key_to_remove)  # Raises `KeyError: 'c'`

您必须注意以下事项:

通过捕获异常:

key_to_remove = "c"
d = {"a": 1, "b": 2}
try:
    del d[key_to_remove]
except KeyError as ex:
    print("No such key: '%s'" % ex.message)

key_to_remove = "c"
d = {"a": 1, "b": 2}
try:
    d.pop(key_to_remove)
except KeyError as ex:
    print("No such key: '%s'" % ex.message)

通过执行检查:

key_to_remove = "c"
d = {"a": 1, "b": 2}
if key_to_remove in d:
    del d[key_to_remove]

key_to_remove = "c"
d = {"a": 1, "b": 2}
if key_to_remove in d:
    d.pop(key_to_remove)

但是pop()还有一种更简洁的方法-提供默认的返回值:

key_to_remove = "c"
d = {"a": 1, "b": 2}
d.pop(key_to_remove, None)  # No `KeyError` here

除非您pop()用来获取要删除的键的值,否则可以提供任何必要的信息None。虽然可能由于with函数本身具有复杂性而导致开销,所以delwith incheck的使用快一些pop()。通常情况并非如此,因此pop()使用默认值就足够了。


对于主要问题,您必须复制字典,以保存原始字典,并在不删除密钥的情况下新建一个字典。

这里的其他一些人建议使用进行完整(较深)的副本copy.deepcopy(),这可能是一个过大的杀伤力,而使用copy.copy()或则dict.copy()可能是“正常”(较浅)的副本,可能就足够了。字典保留对对象的引用作为键的值。因此,当您从字典中删除键时,该引用将被删除,而不是被引用的对象。如果内存中没有其他引用,则垃圾回收器随后可以自动删除该对象本身。与浅拷贝相比,进行深拷贝需要更多的计算,因此,通过进行深拷贝,浪费内存并为GC提供更多工作,它会降低代码性能,有时浅拷贝就足够了。

但是,如果您将可变对象作为字典值,并计划以后在不带键的情况下在返回的字典中对其进行修改,则必须进行深拷贝。

使用浅拷贝:

def get_dict_wo_key(dictionary, key):
    """Returns a **shallow** copy of the dictionary without a key."""
    _dict = dictionary.copy()
    _dict.pop(key, None)
    return _dict


d = {"a": [1, 2, 3], "b": 2, "c": 3}
key_to_remove = "c"

new_d = get_dict_wo_key(d, key_to_remove)
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3], "b": 2}
new_d["a"].append(100)
print(d)  # {"a": [1, 2, 3, 100], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2}
new_d["b"] = 2222
print(d)  # {"a": [1, 2, 3, 100], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2222}

使用深拷贝:

from copy import deepcopy


def get_dict_wo_key(dictionary, key):
    """Returns a **deep** copy of the dictionary without a key."""
    _dict = deepcopy(dictionary)
    _dict.pop(key, None)
    return _dict


d = {"a": [1, 2, 3], "b": 2, "c": 3}
key_to_remove = "c"

new_d = get_dict_wo_key(d, key_to_remove)
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3], "b": 2}
new_d["a"].append(100)
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2}
new_d["b"] = 2222
print(d)  # {"a": [1, 2, 3], "b": 2, "c": 3}
print(new_d)  # {"a": [1, 2, 3, 100], "b": 2222}

20

…如何从字典中删除项目以返回副本(即不修改原始内容)?

A dict是用于此的错误数据结构。

当然,复制dict并从复制中弹出是可行的,利用理解力构建新dict也是如此,但是所有复制都需要时间-您已经用线性时间操作替换了恒定时间操作。并且所有这些活着的副本立刻占据了空间-每个副本的线性空间。

其他数据结构(例如哈希数组映射尝试)也正是针对这种用例而设计的:添加或删除元素会以对数时间返回一个副本并将其大部分存储与原始共享1个

当然也有一些缺点。性能是对数而不是常数(尽管基数较大,通常为32-128)。而且,尽管您可以使非变异API与相同dict,但“变异” API显然是不同的。而且,最重要的是,Python不附带HAMT电池。2

pyrsistent库是基于HAMT的dict-replacement(以及各种其他类型)的Python相当可靠的实现。它甚至还有一个漂亮的Evolutioner API,用于尽可能平滑地将现有的变异代码移植到持久代码中。但是,如果您想明确地表示要返回副本而不是进行变异,则可以像这样使用它:

>>> from pyrsistent import m
>>> d1 = m(a=1, b=2)
>>> d2 = d1.set('c', 3)
>>> d3 = d1.remove('a')
>>> d1
pmap({'a': 1, 'b': 2})
>>> d2
pmap({'c': 3, 'a': 1, 'b': 2})
>>> d3
pmap({'b': 2})

d3 = d1.remove('a')正是这个问题所要的。

如果您有可变的数据结构(例如)dictlist嵌入到中pmap,则仍然会出现别名问题-您只能通过将pmaps和pvectors 嵌入所有位置来实现不可变,以解决此问题。


1. HAMT在Scala,Clojure和Haskell等语言中也很流行,因为它们在无锁编程和软件事务存储中的表现非常好,但是在Python中它们都不重要。

2.事实上,在一个STDLIB HAMT,在执行中使用contextvars较早撤消的PEP解释了原因。但这是库的隐藏实现细节,而不是公共集合类型。


19
d = {1: 2, '2': 3, 5: 7}
del d[5]
print 'd = ', d

结果: d = {1: 2, '2': 3}


14

只需调用del d ['key']。

但是,在生产中,始终最好检查d中是否存在“密钥”。

if 'key' in d:
    del d['key']

7
嗯,不,在生产中最好遵循EAFP意识形态。只需删除钥匙try-except。至少,这将是一个原子操作;)
maxkoryukov

1
而且,如果您想简洁明了,请使用d.pop('key', None),它是oneliner。但是实际的问题是关于不带一把键的字典,而不是修改字典。因此,理解力 -在这里是一个不错的选择;)
maxkoryukov

7

不,除了

def dictMinus(dct, val):
   copy = dct.copy()
   del copy[val]
   return copy

但是,通常仅创建略有变化的字典的副本可能不是一个好主意,因为这将导致相对较大的内存需求。通常最好记录旧字典(如果需要的话),然后对其进行修改。


7
# mutate/remove with a default
ret_val = body.pop('key', 5)
# no mutation with a default
ret_val = body.get('key', 5)

5
>>> def delete_key(dict, key):
...     del dict[key]
...     return dict
... 
>>> test_dict = {'one': 1, 'two' : 2}
>>> print delete_key(test_dict, 'two')
{'one': 1}
>>>

这不会进行任何错误处理,它假定键在字典中,您可能需要先检查一下,raise如果没有


10
您的方法与刚刚的方法有何不同del test_dict[key]
疯狂物理学家,2015年

5

这里是一种顶层设计方法:

def eraseElement(d,k):
    if isinstance(d, dict):
        if k in d:
            d.pop(k)
            print(d)
        else:
            print("Cannot find matching key")
    else:
        print("Not able to delete")


exp = {'A':34, 'B':55, 'C':87}
eraseElement(exp, 'C')

我正在将字典和想要的键传递到函数中,验证它是否是字典,并且键是否还可以,如果两者都存在,则从字典中删除值并打印出剩余的值。

输出: {'B': 55, 'A': 34}

希望有帮助!


3

下面的代码段绝对会帮助您,我在每一行中添加了注释,这将有助于您理解代码。

def execute():
   dic = {'a':1,'b':2}
   dic2 = remove_key_from_dict(dic, 'b')  
   print(dict2)           # {'a': 1}
   print(dict)            # {'a':1,'b':2}

def remove_key_from_dict(dictionary_to_use, key_to_delete):
   copy_of_dict = dict(dictionary_to_use)     # creating clone/copy of the dictionary
   if key_to_delete in copy_of_dict :         # checking given key is present in the dictionary
       del copy_of_dict [key_to_delete]       # deleting the key from the dictionary 
   return copy_of_dict                        # returning the final dictionary

或者你也可以使用dict.pop()

d = {"a": 1, "b": 2}

res = d.pop("c")  # No `KeyError` here
print (res)       # this line will not execute

或更好的方法是

res = d.pop("c", "key not found")
print (res)   # key not found
print (d)     # {"a": 1, "b": 2}

res = d.pop("b", "key not found")
print (res)   # 2
print (d)     # {"a": 1}

2

这是使用列表理解的另一个变体:

original_d = {'a': None, 'b': 'Some'}
d = dict((k,v) for k, v in original_d.iteritems() if v)
# result should be {'b': 'Some'}

该方法基于本文的答案:一种 有效的方法来从字典中删除带有空字符串的键


1
如果您要回答一个已经存在简单,适当且已接受的答案的古老问题,至少要确保您的答案是正确的。这不符合OP的要求。
user2357112在2013年

我通常不会检查我认为可能已添加了有价值信息的问题的日期。此外,根据我对问题的评论之一,它链接到:“通常,这正是某人想要的,可能是OP所需要的,但这不是OP所要求的” stackoverflow.com/questions/12118695/…我知道这不是问题的直接答案;而是对选项的扩展。
BigBlueHat

3
这个答案虽然不完整,但让我们了解到,如果条件允许,我们也可以删除项目。只是改变if vif k is not 'a'回答操作。但是我不认为这是一种有效的方法,这会像pop或del那样删除O(n)中的元素,而不是O(log n)。
holgac 2014年

0
    species = {'HI': {'1': (1215.671, 0.41600000000000004),
  '10': (919.351, 0.0012),
  '1025': (1025.722, 0.0791),
  '11': (918.129, 0.0009199999999999999),
  '12': (917.181, 0.000723),
  '1215': (1215.671, 0.41600000000000004),
  '13': (916.429, 0.0005769999999999999),
  '14': (915.824, 0.000468),
  '15': (915.329, 0.00038500000000000003),
 'CII': {'1036': (1036.3367, 0.11900000000000001), '1334': (1334.532, 0.129)}}

以下代码将复制字典species并删除不在目录中的项目trans_HI

trans_HI=['1025','1215']
for transition in species['HI'].copy().keys():
    if transition not in trans_HI:
        species['HI'].pop(transition)
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.