从字典中删除元素

问题描述 投票:1096回答:14

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

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

python dictionary pop del
14个回答
1476
投票

del statement删除了一个元素:

del d[key]

但是,这会改变现有字典,因此字典的内容会针对具有对同一实例的引用的任何其他人进行更改。要返回新字典,请复制字典:

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

dict()构造函数生成浅表副本。要制作深层副本,请参阅copy module


请注意,为每个dict del / assignment / etc制作副本。意味着你从恒定时间到线性时间,也使用线性空间。对于小的dicts,这不是问题。但是如果你打算制作大量的dicts副本,你可能想要一个不同的数据结构,比如HAMT(如this answer中所述)。


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

这不做任何错误处理,它假设键在dict中,你可能想先检查一下,raise如果不是


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}

希望有所帮助!


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

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'}

该方法基于这篇文章的回答:Efficient way to remove keys with empty strings from a dict


195
投票

pop改变了字典。

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

如果你想保留原件,你可以复制它。


66
投票

我认为您的解决方案是最好的方法。但是如果你想要另一个解决方案,你可以使用旧词典中的键创建一个新词典而不包括你指定的键,如下所示:

>>> 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'}

51
投票

del statement是你正在寻找的。如果你有一个名为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

41
投票

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

您可以使用dict.pop()方法和更通用的del statement从字典中删除项目。它们都会改变原始字典,因此您需要制作副本(请参阅下面的详细信息)。

如果你提供给它们的密钥不在字典中,它们都会引发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。虽然使用delin检查可能会稍微快一点,因为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}

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

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


13
投票

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

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

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

13
投票

...如何从字典中删除项目以返回副本(即,不修改原始文件)?

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

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

其他数据结构,如hash array mapped tries,正是为这种用例而设计的:添加或删除元素会以对数时间返回副本,并将其大部分存储空间与原始数据共享。

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

pyrsistent库是基于HAMT的dict替换(以及各种其他类型)的非常可靠的实现。它甚至有一个漂亮的evolver 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')正是问题所要求的。

如果你有dict中嵌入的listpmap这样的可变数据结构,你仍然会有别名问题 - 你只能通过不断变化来修复它,嵌入pmaps和pvectors。


1. HAMT在Scala,Clojure,Haskell等语言中也很受欢迎,因为它们在无锁编程和软件事务内存方面表现非常出色,但这些都与Python无关。

2.实际上,stdlib中有一个HAMT,用于实现contextvarsThe earlier withdrawn PEP explains why.但这是库的隐藏实现细节,而不是公共集合类型。


7
投票

不,没有别的办法了

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

但是,经常创建只有略微改变的字典的副本可能不是一个好主意,因为它会导致相对较大的内存需求。通常最好记录旧字典(如果需要),然后修改它。

© www.soinside.com 2019 - 2024. All rights reserved.