从字典中删除元素

ric*_*lla 1243 python dictionary del

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

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

Gre*_*ill 1578

del语句删除了一个元素:

del d[key]
Run Code Online (Sandbox Code Playgroud)

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

def removekey(d, key):
    r = dict(d)
    del r[key]
    return r
Run Code Online (Sandbox Code Playgroud)

dict()构造使得浅拷贝.要进行深层复制,请参阅该copy模块.


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

  • @tMC如果你在循环它时编辑`dict`,它会给你一个错误:`RuntimeError:字典在迭代期间改变了大小 (26认同)
  • 这个答案有一个弱点,可能会产生误导.读者可能会误解dict(d)可以用'd'给他们一份副本.但这是一份不完整的副本.当只做del键操作时,那没关系.但是当你想对嵌套的dict执行其他操作时,使用该复制方法修改"r"可能会导致更改原始的"d".要获得真实的副本,首先需要'导入副本',然后'r = copy.deepcopy(d)'. (21认同)
  • 那么```pop```方法实际上也是如此呢?不是更pythonic?(是dict的方法,不是特别的保留字)? (15认同)
  • 这是关于字典+1的可变性的一个很好的观点 - 虽然我想不出我想要字典副本的时候,我总是依赖'每个人'的副本是一样的.好极了. (14认同)
  • @Zen:很公道,我已经添加了关于浅版和深版的注释. (10认同)
  • @GregHewgill,我知道,所以这不是一个严重的弱点.但是既然你提到"要返回一个新词典,请复制一本词典:".凭借358K点和143095次观看时间,这可能会误导相当多的python初学者.顺便说一句,我自己之前被这篇文章误导了. (2认同)

Cry*_*tal 220

pop 改变字典.

 >>>lol = {"hello":"gdbye"}
 >>>lol.pop("hello")
    'gdbye'
 >>> lol
     {}
Run Code Online (Sandbox Code Playgroud)

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

  • "del"是好的,但在我看来,"pop"似乎更像是"Pythonic". (16认同)
  • @ivanleoncz 还有一个更好的原因,“pop”可以提供一个默认值,当 dict 中缺少某个键时将返回该默认值。当您需要移除一些钥匙但其中一些钥匙可能丢失时,这很有用;在这种情况下,“del”会抛出“KeyError”。 (15认同)
  • `pop`返回'popped'的值,这允许您使用此值进一步的原因.如果它不是"Pythonic",我会说这似乎更好,肯定:).它不是一个字典,但它对两者都有相同的作用:https://github.com/ivanlmj/python-prototypes/blob/master/3.4/pop_list.py (3认同)
  • 也适用于`lambda`s(而`del`不适用). (2认同)
  • @ivanleoncz 为什么? (2认同)

utd*_*mir 71

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

>>> 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'}
Run Code Online (Sandbox Code Playgroud)

  • 更好的是`{k:v for k,v in a.items()如果k!= 0}`我想. (8认同)
  • 对于那些不熟悉的解析,你也可以做这样的事情:`{我:一个为我在[我]一,如果我不能在[0,1,2]}`如果你想删除的几个要素. (5认同)
  • 真的很酷.我喜欢在不定义新功能的情况下过滤字典的快速方法. (2认同)

aru*_*l84 52

del语句是你在找什么.如果你有一个名为foo的词典,带有一个名为'bar'的键,你可以从foo中删除'bar',如下所示:

del foo['bar']
Run Code Online (Sandbox Code Playgroud)

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

>>> foo = {'bar': 'baz'}
>>> fu = dict(foo)
>>> del foo['bar']
>>> print foo
{}
>>> print fu
{'bar': 'baz'}
Run Code Online (Sandbox Code Playgroud)

这个dict电话是一个浅薄的副本.如果您想要深层复制,请使用copy.deepcopy.

为方便起见,这是一种可以复制和粘贴的方法:

def minus_key(key, dictionary):
    shallow_copy = dict(dictionary)
    del shallow_copy[key]
    return shallow_copy
Run Code Online (Sandbox Code Playgroud)

  • 为方便起见,我添加了一个可以复制/粘贴的功能. (3认同)
  • @ pythonian29033关于`>>>`.是的,它是REPL风格,但让我们坦率地说:只有一个人写过这个样本,1000个人已经读过这个.我认为,以允许轻松复制和运行的方式编写示例会很棒.我不想用手去掉这个尖括号.或者逐行复制..所以我不明白:为什么这个角度还在那里)))可能是我不知道什么? (2认同)

Nik*_*ita 47

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

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

KeyError如果你提供给他们的密钥不在字典中,他们都会提出一个:

key_to_remove = "c"
d = {"a": 1, "b": 2}
del d[key_to_remove]  # Raises `KeyError: 'c'`
Run Code Online (Sandbox Code Playgroud)

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

你必须照顾这个:

通过捕获异常:

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)
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

通过检查:

key_to_remove = "c"
d = {"a": 1, "b": 2}
if key_to_remove in d:
    del d[key_to_remove]
Run Code Online (Sandbox Code Playgroud)

key_to_remove = "c"
d = {"a": 1, "b": 2}
if key_to_remove in d:
    d.pop(key_to_remove)
Run Code Online (Sandbox Code Playgroud)

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

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

除非您使用pop()以获取密钥的值,否则您可以提供任何内容,而不是必需的None.虽然使用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}
Run Code Online (Sandbox Code Playgroud)

深层复制:

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}
Run Code Online (Sandbox Code Playgroud)


小智 28

使用 del 您可以删除传递该值的键的字典值

链接: del方法

del dictionary['key_to_del']
Run Code Online (Sandbox Code Playgroud)


sat*_*els 19

d = {1: 2, '2': 3, 5: 7}
del d[5]
print 'd = ', d
Run Code Online (Sandbox Code Playgroud)

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


aba*_*ert 16

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

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

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

其他数据结构(如散列数组映射尝试)专门针对这种用例而设计:添加或删除元素会以对数时间返回副本,并将其大部分存储与原始文件共享.1

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

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})
Run Code Online (Sandbox Code Playgroud)

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

如果你有可变数据结构,dict并且list嵌入了pmap,你仍然会有别名问题 - 你只能通过一直向下,嵌入pmaps和pvectors 来解决这个问题.


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

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

  • [`immutables`](https://github.com/MagicStack/immutables) 包现已推出。 (2认同)

Kha*_*Hua 14

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

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

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

  • 嗯,不,在生产中,最好遵循[EAFP](https://docs.python.org/2/glossary.html#term-eafp)的意识形态.只需删除`try-except`块中的键即可.至少,这将是一个原子操作;) (6认同)

phi*_*hag 7

不,没有别的办法了

def dictMinus(dct, val):
   copy = dct.copy()
   del copy[val]
   return copy
Run Code Online (Sandbox Code Playgroud)

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


dai*_*no3 7

# mutate/remove with a default
ret_val = body.pop('key', 5)
# no mutation with a default
ret_val = body.get('key', 5)
Run Code Online (Sandbox Code Playgroud)


Moh*_*eza 7

解决方案1:删除

info = {'country': 'Iran'}
country = info.pop('country') if 'country' in info else None
Run Code Online (Sandbox Code Playgroud)

解决方案2:不删除

info = {'country': 'Iran'}
country = info.get('country') or None
Run Code Online (Sandbox Code Playgroud)


May*_*wal 6

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

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
Run Code Online (Sandbox Code Playgroud)

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

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

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

或者更好的方法是

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}
Run Code Online (Sandbox Code Playgroud)


tMC*_*tMC 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}
>>>
Run Code Online (Sandbox Code Playgroud)

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

  • 你的方法与`del test_dict [key]`有什么不同? (10认同)

lox*_*sat 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')
Run Code Online (Sandbox Code Playgroud)

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

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

希望有帮助!