【问题标题】:Delete an element from a dictionary从字典中删除一个元素
【发布时间】:2011-08-16 05:23:55
【问题描述】:

有没有办法在 Python 中从字典中删除一个项目?

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

【问题讨论】:

  • 既然可以直接修改字典,为什么还需要一个返回字典的函数呢?
  • dictionary pop method 更改字典就地。因此,它改变了对从调用者传递到“帮助函数”的字典的reference。所以“辅助函数”不需要返回任何东西,因为调用者中对字典的原始引用已经被改变了。如果您不需要,不要将来自dict.pop() 的返回分配给任何东西。例如:do stuff with my_dict; my_dict.pop(my_key, None); do more stuff with my_dict # now doesn't have my_key。如果需要,请使用deepcopy(my_dict)
  • 由于原标题不同意细节,特意排除了明显的解决方案d.pop(),所以我固定标题问细节中指定的问题。
  • 我们应该添加一个警告,询问您是否真的想要这样做,就好像您在带有 E 元素的字典上执行 N 次您会泄漏(/使用)包含所有深拷贝的 O(N*E) 内存。如果您只想要一个只读(浅拷贝),请执行d.pop(key)。但是,如果有任何东西修改了浅拷贝,你就有了well-known problem with aliasing。如果您告诉我们更广泛的背景,它会有所帮助。 (还有其他任何东西修改过 dict 值吗?您是否试图破坏性地迭代列表?如果没有,那是什么?)
  • "当您可以直接修改字典时,为什么需要一个返回字典的函数?"也许是因为您想编写修改其参数的纯函数?

标签: python dictionary del


【解决方案1】:

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

【讨论】:

  • @pythonian29033 实际上,没有。接受的答案按预期工作 - 它返回没有一个键的字典。这个答案的方法改变了原始的dict;)有一个显着的区别
  • @arussell84,为什么>>>经常在python-examples中使用?是的,python-doc 包含很多这样的东西。但是这样的代码不方便复制粘贴。我很困惑...
  • @maxkoryukov 是的!但是那个函数和这个答案是完全一样的,除了那个答案在一个函数里面。而且你肯定有一段时间没有在 python 中编码了,>>> 在 cli 模式下模仿 python 的监听符号
  • @pythonian29033 关于>>>。是的,它是 REPL 风格的,但让我们坦率地说:只有一个人写过这个样本,而 1000 人读过这个。我认为,以易于复制和运行的方式编写示例会很棒。我不喜欢手动删除这个尖括号。或者逐行复制..所以我不明白:为什么这个角度仍然存在)))可能是我不知道什么?
  • 为了您的方便,我添加了一个可以复制/粘贴的功能。
【解决方案2】:
d = {1: 2, '2': 3, 5: 7}
del d[5]
print 'd = ', d

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

【讨论】:

    【解决方案3】:

    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 的副本,您可能需要不同的数据结构,例如 HAMT(如 this answer 中所述)。

    【讨论】:

    • 关于字典的可变性 +1 这很重要 - 虽然我想不出我想要字典副本的时间,但我一直依赖“每个人”的副本都是一样的.好点。
    • @tMC 如果您在循环遍历 dict 时对其进行编辑,则会出现错误:RuntimeError: dictionary changed size during iteration
    • pop 方法实际上是做什么的呢?它不是更pythonic吗? (是dict的方法,不是特殊的保留字)?
    • 这个答案有一个弱点,它可能会产生误导。读者可能会误解 dict(d) 可以给他们一个带有 'd' 的副本。但它是一个不完整的副本。只做del键操作时,没关系。但是当你想对嵌套的 dict 做其他事情时,使用该复制方法修改 'r' 可能会导致对原始 'd' 的更改。要获得正版,首先需要'import copy',然后'r = copy.deepcopy(d)'。
    • @GregHewgill,我知道,所以这不是一个严重的弱点。但是由于您提到“要返回新字典,请复制字典:”。凭借您的 358K 点和 143095 次查看时间,这可能会误导大量 Python 初学者。顺便说一句,我自己之前被这个帖子误导了。
    【解决方案4】:
    >>> 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

    【讨论】:

    • 你的方法和del test_dict[key]有什么不同?
    【解决方案5】:

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

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

    【讨论】:

    • 真的很酷。我喜欢在不定义新函数的情况下过滤字典的快速方法。
    • 如果你不熟悉推导式,你也可以这样做:{i:a[i] for i in a if i not in [0, 1, 2]} 如果你想删除几个元素。
    • 我认为最好是{k:v for k,v in a.items() if k != 0}
    • 按键删除项目并在同一行返回新dict的结果的最佳解决方案。例如,如果您需要使用已构建的没有单个项目的 dict 作为 **kwargs, some_function(**{k:v for k,v in some_dict.items() if k not 'some_key'})
    • 这里的最佳解决方案。一个班轮,它不会改变原始字典。
    【解决方案6】:

    不,没有别的办法

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

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

    【讨论】:

      【解决方案7】:

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

      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

      【讨论】:

      • 如果您要回答一个已有多年历史且已经有一个简单、适当、可接受的答案的问题,至少要确保您的答案是正确的。这不符合 OP 的要求。
      • 我通常不会检查我认为可能会添加有价值信息的问题的日期。此外,根据我链接到的问题中的一位 cmets:“通常这正是某人想要的,并且可能是 OP 需要的,但这不是 OP 所要求的”stackoverflow.com/questions/12118695/… 我知道这不是直接回答问题;而是对选项的扩展。
      • 这个答案虽然不完整,但让我们了解到我们也可以删除带有 if 条件的项目。只需将if v 更改为if k is not 'a' 即可回答操作。但我认为这不是一种有效的方法,这会删除 O(n) 中的元素,而不是像 pop 或 del 那样删除 O(log n) 中的元素。
      • @holgac 复制字典在 O(n) 中运行,所以你关于效率的论点是不正确的,因为 OP 要求原始字典保持不变,这需要复制字典。
      【解决方案8】:

      pop 改变字典。

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

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

      【讨论】:

      • "del" 没问题,但在我看来,"pop" 似乎更“Pythonic”。
      • @ivanleoncz 为什么?
      • pop 返回“弹出”的值,这允许您出于任何其他原因使用此值。如果它不是更“Pythonic”,我会说这看起来更好,当然:)。这不是 dict,但两者的工作方式相同:github.com/ivanlmj/python-prototypes/blob/master/3.4/…
      • @ivanleoncz 还有一个更好的原因,pop 可以提供一个默认值,当字典中缺少键时将返回该默认值。当您需要删除一些键但其中一些可能丢失时,这很好; del 在这种情况下会抛出 KeyError
      【解决方案9】:

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

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

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

      【讨论】:

      • 嗯,不,在生产中最好遵循EAFP 意识形态。只需删除 try-except 块中的密钥。至少,这将是一个原子操作;)
      • 如果你想简洁——使用d.pop('key', None),它是一个单行。但实际的问题是关于没有一键获取字典,而不是修改字典。所以comprehensions - 这里是个不错的选择;)
      【解决方案10】:

      这里是顶级设计方法:

      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}

      希望有帮助!

      【讨论】:

        【解决方案11】:
        # mutate/remove with a default
        ret_val = body.pop('key', 5)
        # no mutation with a default
        ret_val = body.get('key', 5)
        

        【讨论】:

          【解决方案12】:

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

          您可以使用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}
          

          【讨论】:

            【解决方案13】:

            下面的代码 sn-p 肯定会对你有所帮助,我在每一行都添加了 cmets,这将有助于你理解代码。

            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}
            

            【讨论】:

              【解决方案14】:

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

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

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

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

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

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

              如果您在pmap 中嵌入了像dictlist 这样的可变数据结构,您仍然会遇到别名问题——您只能通过一直不可变、嵌入@987654334 来解决这个问题@s 和pvectors。


              1。 HAMT 在 Scala、Clojure、Haskell 等语言中也很流行,因为它们在无锁编程和软件事务内存方面表现得非常好,但这些都与 Python 无关。

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

              【讨论】:

                【解决方案15】:
                    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)}}
                

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

                trans_HI=['1025','1215']
                for transition in species['HI'].copy().keys():
                    if transition not in trans_HI:
                        species['HI'].pop(transition)
                

                【讨论】:

                  【解决方案16】:

                  可以试试我的方法。一行。

                  yourList = [{'key':'key1','version':'1'},{'key':'key2','version':'2'},{'key':'key3','version':'3'}]
                  resultList = [{'key':dic['key']} for dic in yourList if 'key' in dic]
                  print(resultList)
                  

                  【讨论】:

                    【解决方案17】:

                    使用 del 可以删除传递该值的键的 dict 值

                    链接: del method

                    del dictionary['key_to_del']
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2021-04-09
                      • 2016-05-06
                      • 2015-03-16
                      相关资源
                      最近更新 更多