【问题标题】:Check if a given key already exists in a dictionary检查给定键是否已存在于字典中
【发布时间】:2010-12-08 20:16:11
【问题描述】:

我想在更新键的值之前测试一个键是否存在于字典中。 我写了以下代码:

if 'key1' in dict.keys():
  print "blah"
else:
  print "boo"

我认为这不是完成这项任务的最佳方式。有没有更好的方法来测试字典中的键?

【问题讨论】:

  • 调用dict.keys() 会根据文档docs.python.org/2/library/stdtypes.html#dict.keys 创建一个键列表,但如果此模式没有针对在认真的实现中进行优化以转换为@987654324,我会感到惊讶@.
  • 所以我终于找到了为什么我的许多 Python 脚本这么慢 :) :(。那是因为我一直在使用 x in dict.keys() 来检查密钥。这是因为通常的方法在 Java 中迭代键是 for (Type k : dict.keySet()),这个习惯导致 for k in dict.keys() 感觉比 for k in dict 更自然(就性能而言应该仍然没问题?),但是检查键也变成了 if k in dict.keys(),这是一个问题...
  • @EvgeniSergeev if k in dict_: 测试 dict_ 的 KEYS 中是否存在 k,因此您仍然不需要 dict_.keys()。 (这让我有点吃惊,因为它读起来就像它在 dict 中测试 value 一样。但事实并非如此。)
  • @ToolmakerSteve 没错,但您不仅不需要它,这不是一个好习惯。
  • 尝试“输入字典”

标签: python dictionary


【解决方案1】:

您不必调用键:

if 'key1' in dict:
  print("blah")
else:
  print("boo")

这将是很多faster,因为它使用字典的散列而不是进行线性搜索,而调用键会这样做。

【讨论】:

    【解决方案2】:

    您可以将代码缩短为:

    if 'key1' in my_dict:
        ...
    

    不过,这充其量只是外观上的改进。为什么你认为这不是最好的方法?

    【讨论】:

    • 不仅仅是外观上的改进。使用这种方法找到一个键的时间是 O(1),而调用键会生成一个列表并且是 O(n)。
    • O(1) 似乎不太正确。你确定这不是 O(log n) 之类的吗?
    • 这是单个 dict 查找的复杂度,平均为 O(1),最坏的情况为 O(n)。 .list() 将始终为 O(n)。 wiki.python.org/moin/TimeComplexity
    • 这也避免了额外的分配。 (对于使紧密循环更快一点很重要)
    【解决方案3】:

    我建议改用setdefault 方法。听起来它会做你想做的一切。

    >>> d = {'foo':'bar'}
    >>> q = d.setdefault('foo','baz') #Do not override the existing key
    >>> print q #The value takes what was originally in the dictionary
    bar
    >>> print d
    {'foo': 'bar'}
    >>> r = d.setdefault('baz',18) #baz was never in the dictionary
    >>> print r #Now r has the value supplied above
    18
    >>> print d #The dictionary's been updated
    {'foo': 'bar', 'baz': 18}
    

    【讨论】:

    • setdefault 与 OP 的问题有什么关系?
    • @hughdbrown “我想在更新键的值之前测试一个键是否存在于字典中。”有时,帖子包含的代码会产生一系列对不完全是最初目标的东西的响应。为了实现第一句话中所述的目标,setdefault 是最有效的方法,即使它不是发布的示例代码的直接替代品。
    【解决方案4】:

    in 是测试dict 中是否存在密钥的预期方法。

    d = {"key1": 10, "key2": 23}
    
    if "key1" in d:
        print("this will execute")
    
    if "nonexistent key" in d:
        print("this will not")
    

    如果你想要一个默认值,你可以随时使用dict.get()

    d = dict()
    
    for i in range(100):
        key = i % 10
        d[key] = d.get(key, 0) + 1
    

    如果您想始终确保任何键的默认值,您可以重复使用 dict.setdefault()collections 模块中的 defaultdict,如下所示:

    from collections import defaultdict
    
    d = defaultdict(int)
    
    for i in range(100):
        d[i % 10] += 1
    

    但总的来说,in 关键字是最好的方法。

    【讨论】:

    • 我通常只使用get,如果我要从字典中提取项目的话。使用in 将项目从字典中拉出是没有意义的。
    • 我完全同意。但是,如果您只需要知道某个键是否存在,或者您需要区分定义键的情况和使用默认键的情况,in 是最好的方法。
    • @enkash 提供了 Python 3 的参考。这里是 Python 2.7 的参考:dictdict.get
    • get 是一个糟糕的测试,如果密钥等同于“False”,例如0。很难学到这一点:/
    • 我不能同意这是一个完整的答案,因为它没有提到当密钥失败的数量足够小时时,'try'-'except' 将是最快的。请参阅下面的答案:stackoverflow.com/a/1602945/4376643
    【解决方案5】:

    您可以使用 in 关键字来测试字典中是否存在某个键:

    d = {'a': 1, 'b': 2}
    'a' in d # <== evaluates to True
    'c' in d # <== evaluates to False
    

    在改变它之前检查字典中的键是否存在的一个常见用途是默认初始化值(例如,如果你的值是列表,例如,并且你想确保有一个空列表到您可以在插入键的第一个值时追加)。在这种情况下,您可能会发现 collections.defaultdict() 类型很有趣。

    在旧代码中,您可能还会发现 has_key() 的一些用途,这是一种已弃用的用于检查字典中键是否存在的方法(请改用 key_name in dict_name)。

    【讨论】:

      【解决方案6】:

      仅限 Python 2:(并且 Python 2.7 已经支持 `in`)

      你可以使用has_key()方法:

      if dict.has_key('xyz')==1:
          #update the value for the key
      else:
          pass
      

      【讨论】:

      • .has_key() 一直是deprecated;您应该使用in,如其他答案所示。
      • 顺便说一句,我建议在回答之前阅读 ALLOLD 问题的现有答案。这个答案没有增加任何内容,因为从 09 年开始,迈克尔的答案中已经存在该建议。 (我并不是要阻止尝试在讨论中添加一些有用的东西。继续尝试。)
      【解决方案7】:

      只是对克里斯的补充。 B(最佳答案):

      d = defaultdict(int)
      

      同样有效;原因是调用int() 会返回0,这是defaultdict 在幕后所做的(在构造字典时),因此文档中的名称为“Factory Function”。

      【讨论】:

      • 如果你正在创建一个计数字典,你应该使用Counter(假设 Python 2.7)。我使用defaultdict(lambda: 0) 而不是defaultdict(int),因为我认为发生了什么更清楚;如果您在没有参数的情况下调用int(),读者不需要知道您得到0。 YMMV。
      【解决方案8】:

      如何使用 EAFP(请求宽恕比请求许可更容易):

      try:
         blah = dict["mykey"]
         # key exists in dict
      except KeyError:
         # key doesn't exist in dict
      

      查看其他 SO 帖子:

      Using try vs if in python

      Checking for member existence in Python

      【讨论】:

      • Try/except 可能会更昂贵,如果密钥可能经常不存在。从您引用的帖子中:“[I]如果您希望 99% 的时间结果实际上包含可迭代的内容,我会使用 try/except 方法。如果异常确实是异常的,它会更快。如果结果是 None超过 50 % 的时间,那么使用 if 可能会更好。[...][A]n if 语句总是会花费你,设置 try/except 块几乎是免费的。但是当实际发生异常时,成本要高得多。” stackoverflow.com/a/1835844/1094092
      【解决方案9】:

      有关已接受答案的建议方法(10m 循环)的速度执行的更多信息:

      • 'key' in mydict 已用时间 1.07 秒
      • mydict.get('key') 已用时间 1.84 秒
      • mydefaultdict['key'] 已用时间 1.07 秒

      因此建议使用indefaultdict 而不是get

      【讨论】:

      • 完全同意get的1.84s是
      【解决方案10】:

      获得结果的方式有:

      哪个更好取决于三件事:

      1. 字典是“通常有键”还是“通常没有键”。
      2. 您是否打算使用 if...else...elseif...else 之类的条件?
      3. 字典有多大?

      阅读更多:http://paltman.com/try-except-performance-in-python-a-simple-test/

      使用 try/block 代替 'in' 或 'if':

      try:
          my_dict_of_items[key_i_want_to_check]
      except KeyError:
          # Do the operation you wanted to do for "key not present in dict".
      else:
          # Do the operation you wanted to do with "key present in dict."
      

      【讨论】:

        【解决方案11】:

        python 中的字典有一个 get('key', default) 方法。因此,如果没有密钥,您可以设置一个默认值。

        values = {...}
        myValue = values.get('Key', None)
        

        【讨论】:

        • get 方法的第二个参数是可选的,如果不包含则默认为None,因此values.get('Key', None)values.get('Key') 相同。
        【解决方案12】:

        Python 字典有一个名为__contains__ 的方法。如果字典有键,此方法将返回 True,否则返回 False。

         >>> temp = {}
        
         >>> help(temp.__contains__)
        
        Help on built-in function __contains__:
        
        __contains__(key, /) method of builtins.dict instance
            True if D has a key k, else False.
        

        【讨论】:

        • 直接致电__contains__ 是非常糟糕的做法。正确的做法是使用in操作符,也就是调用__contains__函数的containment check
        • @user1767754 我正在使用foo = x['foo'] if x.__contains__('foo') else 'bar'。任何想法如何将in 运算符用作此表达式的一部分?
        • foo = x['foo'] if 'foo' in x else 'bar'
        【解决方案13】:

        使用python三元运算符:

        message = "blah" if 'key1' in my_dict else "booh"
        print(message)
        

        【讨论】:

          【解决方案14】:

          您可以使用for循环遍历字典并获取要在字典中查找的键的名称,然后使用if条件检查它是否存在:

          dic = {'first' : 12, 'second' : 123}
          for each in dic:
              if each == 'second': 
                  print('the key exists and the corresponding value can be updated in the dictionary')
          

          【讨论】:

          • 检查代码,因为它的输出是 it is existnot exist
          • 如果要执行线性搜索,为什么还要使用字典?
          【解决方案15】:

          分享另一种使用布尔运算符检查键是否存在的方法。

          d = {'a': 1, 'b':2}
          keys = 'abcd'
          
          for k in keys:
              x = (k in d and 'blah') or 'boo'
              print(x) 
          

          返回

          >>> blah
          >>> blah
          >>> boo
          >>> boo
          

          说明

          首先您应该知道,在 Python 中,0None 或零长度对象的计算结果为 False。其他一切都评估为True。布尔运算从左到右求值,返回的操作数不是 True 或 False。

          我们来看一个例子:

          >>> 'Some string' or 1/0 
          'Some string'
          >>>
          

          由于 'Some string' 的计算结果为 True,因此不会计算 or 的其余部分,并且不会引发除以零错误。

          但是如果我们切换顺序,1/0 首先被评估并引发异常:

          >>> 1/0 or 'Some string'
          Traceback (most recent call last):
            File "<stdin>", line 1, in <module>
          ZeroDivisionError: division by zero
          >>> 
          

          我们可以使用这个模式来检查一个键是否存在。

          (k in d and 'blah')
          

          做同样的事情

          if k in d:
              'blah'
          else:
              False
          

          如果键存在,这已经返回正确的结果,但我们希望它在不存在时打印“boo”。因此,我们将结果和or'boo' 一起获取

          >>> False or 'boo'
          'boo'
          >>> 'blah' or 'boo'
          'blah'
          >>> 
          

          【讨论】:

            【解决方案16】:

            检查给定键是否已存在于字典中

            为了了解如何做到这一点,我们首先检查我们可以在字典上调用哪些方法。 方法如下:

            d={'clear':0, 'copy':1, 'fromkeys':2, 'get':3, 'items':4, 'keys':5, 'pop':6, 'popitem':7, 'setdefault':8, 'update':9, 'values':10}
            

            Python Dictionary clear()       Removes all Items
            Python Dictionary copy()        Returns Shallow Copy of a Dictionary
            Python Dictionary fromkeys()    Creates dictionary from given sequence
            Python Dictionary get()         Returns Value of The Key
            Python Dictionary items()       Returns view of dictionary (key, value) pair
            Python Dictionary keys()        Returns View Object of All Keys
            Python Dictionary pop()         Removes and returns element having given key
            Python Dictionary popitem()     Returns & Removes Element From Dictionary
            Python Dictionary setdefault()  Inserts Key With a Value if Key is not Present
            Python Dictionary update()      Updates the Dictionary 
            Python Dictionary values()      Returns view of all values in dictionary
            

            检查密钥是否已经存在的残酷方法可能是get()方法:

            d.get("key")
            

            另外两个有趣方法items()keys() 听起来工作量太大。因此,让我们检查一下get() 是否适合我们。我们有我们的字典d:

            d= {'clear':0, 'copy':1, 'fromkeys':2, 'get':3, 'items':4, 'keys':5, 'pop':6, 'popitem':7, 'setdefault':8, 'update':9, 'values':10}
            

            打印显示我们没有的密钥将返回None

            print(d.get('key')) #None
            print(d.get('clear')) #0
            print(d.get('copy')) #1
            

            我们可以使用它来获取密钥是否存在的信息。 但是,如果我们使用单个 key:None 创建一个字典,请考虑这一点:

            d= {'key':None}
            print(d.get('key')) #None
            print(d.get('key2')) #None
            

            导致get() 方法不可靠,以防某些值可能是None。 这个故事应该有一个更幸福的结局。如果我们使用in 比较器:

            print('key' in d) #True
            print('key2' in d) #False
            

            我们得到了正确的结果。 我们可以检查 Python 字节码:

            import dis
            dis.dis("'key' in d")
            #   1           0 LOAD_CONST               0 ('key')
            #               2 LOAD_NAME                0 (d)
            #               4 COMPARE_OP               6 (in)
            #               6 RETURN_VALUE
            
            dis.dis("d.get('key2')")
            #   1           0 LOAD_NAME                0 (d)
            #               2 LOAD_METHOD              1 (get)
            #               4 LOAD_CONST               0 ('key2')
            #               6 CALL_METHOD              1
            #               8 RETURN_VALUE
            

            这表明in 比较运算符不仅比get() 更可靠,而且速度更快。

            【讨论】:

            • .get() 可以为default 值提供第二个参数,用于处理key:None 的问题。例如:d.get("key", False)
            • .get() 是最快的方法。另一种选择是在 try/except 块中分配
            猜你喜欢
            • 2010-10-03
            • 2020-02-20
            • 1970-01-01
            • 1970-01-01
            • 2022-06-14
            • 2018-07-28
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多