【问题标题】:How do I check that multiple keys are in a dict in a single pass?如何一次检查多个键是否在一个字典中?
【发布时间】:2010-11-20 03:09:14
【问题描述】:

我想做这样的事情:

foo = {
    'foo': 1,
    'zip': 2,
    'zam': 3,
    'bar': 4
}

if ("foo", "bar") in foo:
    #do stuff

如何检查foobar 是否都在字典foo 中?

【问题讨论】:

    标签: python dictionary


    【解决方案1】:

    这应该可行:

    if all(key in foo for key in ["foo","bar"]):
        # do stuff
        pass
    

    提示:

    all() 中使用方括号进行列表理解:

    if all([key in foo for key in ["foo","bar"]]):
    

    不仅没有必要,而且是有害的,因为它们会阻碍all() 的正常短路行为。

    【讨论】:

      【解决方案2】:

      又短又甜

      {"key1", "key2"} <= {*dict_name}

      【讨论】:

      • 您的答案似乎与许多其他答案相同
      【解决方案3】:

      检查字典中是否存在所有键:

      {'key_1', 'key_2', 'key_3'} <= set(my_dict)
      

      检查字典中是否存在一个或多个键:

      {'key_1', 'key_2', 'key_3'} & set(my_dict)
      

      【讨论】:

        【解决方案4】:

        另一种检测是否所有键都在字典中的选项:

        dict_to_test = { ... }  # dict
        keys_sought = { "key_sought_1", "key_sought_2", "key_sought_3" }  # set
        
        if keys_sought & dict_to_test.keys() == keys_sought: 
            # True -- dict_to_test contains all keys in keys_sought
            # code_here
            pass
        

        【讨论】:

          【解决方案5】:

          在确定是否只有某些键匹配的情况下,这是有效的:

          any_keys_i_seek = ["key1", "key2", "key3"]
          
          if set(my_dict).intersection(any_keys_i_seek):
              # code_here
              pass
          

          如果只有一些键匹配,还有另一个选项:

          any_keys_i_seek = ["key1", "key2", "key3"]
          
          if any_keys_i_seek & my_dict.keys():
              # code_here
              pass
          

          【讨论】:

            【解决方案6】:
            if {"foo", "bar"} <= myDict.keys(): ...
            

            如果你还在使用 Python 2,你可以这样做

            if {"foo", "bar"} <= myDict.viewkeys(): ...
            

            如果您仍然使用真正旧 Python set,但它会遍历整个 dict 来构建集合,这就是慢:

            if set(("foo", "bar")) <= set(myDict): ...
            

            【讨论】:

            • 看起来不错!我唯一不喜欢的是你必须创建临时集合,但它非常紧凑。所以我必须说......很好地使用集合!
            • 在 python 3 中,您可以说set(("foo","bar")) &lt;= myDict.keys(),它避免了临时设置,因此速度更快。对于我的测试,当查询为 10 个项目时,它与使用 all 的速度大致相同。但随着查询变大,它会变慢。
            • 我已经发布了一些测试作为答案。 stackoverflow.com/questions/1285911/…
            • if {'foo', 'bar'} &lt;= set(myDict): ...
            • 对于任何想知道为什么会这样的人:运算符 docs.python.org/3/library/stdtypes.html#set-types-set-frozenset
            【解决方案7】:

            这只是我的看法,有两种方法很容易理解所有给定的选项。所以我的主要标准是有非常可读的代码,而不是特别快的代码。为了使代码易于理解,我更喜欢给出可能性:

            • var
            • var.issubset(var2)

            “var

            import timeit
            
            timeit.timeit('var <= var2.keys()', setup='var={"managed_ip", "hostname", "fqdn"}; var2= {"zone": "test-domain1.var23.com", "hostname": "bakje", "api_client_ip": "127.0.0.1", "request_data": "", "request_method": "GET", "request_url": "hvar2p://127.0.0.1:5000/test-domain1.var23.com/bakje", "utc_datetime": "04-Apr-2019 07:01:10", "fqdn": "bakje.test-domain1.var23.com"}; var={"managed_ip", "hostname", "fqdn"}')
            0.1745898080000643
            
            timeit.timeit('var.issubset(var2)', setup='var={"managed_ip", "hostname", "fqdn"}; var2= {"zone": "test-domain1.var23.com", "hostname": "bakje", "api_client_ip": "127.0.0.1", "request_data": "", "request_method": "GET", "request_url": "hvar2p://127.0.0.1:5000/test-domain1.var23.com/bakje", "utc_datetime": "04-Apr-2019 07:01:10", "fqdn": "bakje.test-domain1.var23.com"}; var={"managed_ip", "hostname", "fqdn"};')
            0.2644960229999924
            

            【讨论】:

              【解决方案8】:

              你也可以使用.issubset()

              >>> {"key1", "key2"}.issubset({"key1":1, "key2":2, "key3": 3})
              True
              >>> {"key4", "key2"}.issubset({"key1":1, "key2":2, "key3": 3})
              False
              >>>
              

              【讨论】:

                【解决方案9】:

                我认为这是最聪明和最简单的。

                {'key1','key2'} <= my_dict.keys()
                

                【讨论】:

                  【解决方案10】:

                  虽然我喜欢 Alex Martelli 的回答,但在我看来它并不像 Pythonic。也就是说,我认为 Pythonic 的一个重要部分是易于理解。有了这个目标,&lt;= 就不容易理解了。

                  虽然字符更多,但按照 Karl Voigtland 的回答建议使用 issubset() 更容易理解。由于该方法可以使用字典作为参数,因此一个简短且易于理解的解决方案是:

                  foo = {'foo': 1, 'zip': 2, 'zam': 3, 'bar': 4}
                  
                  if set(('foo', 'bar')).issubset(foo):
                      #do stuff
                  

                  我想用{'foo', 'bar'} 代替set(('foo', 'bar')),因为它更短。但是,这不是那么容易理解,而且我认为大括号太容易混淆为字典。

                  【讨论】:

                  • 我觉得明白了意思就可以理解了。
                  • in the documentation.issubset() 的同义词。我认为在 Python 文档中使其默认为 Pythonic。
                  【解决方案11】:

                  您不必将左侧包裹在一组中。你可以这样做:

                  if {'foo', 'bar'} <= set(some_dict):
                      pass
                  

                  这也比all(k in d...) 解决方案执行得更好。

                  【讨论】:

                  • 这也比 all(k in d...) 解决方案执行得更好。我建议将此作为编辑,但以better to add a comment为由拒绝了。所以我就是这样做的
                  • @miraculixx 添加评论不是更好。最好将相关信息编辑成答案并删除cmets。
                  • @endolith 我同意,有些人显然不像你在我最初所做的拒绝编辑中看到的那样。无论如何,这是关于元的讨论,而不是在这里。
                  • 有人能解释一下吗?我已经收集到 {} 创建了一个集合,但是小于或等于运算符在这里如何工作?
                  • @Locane docs.python.org/2/library/sets.html
                  【解决方案12】:
                  >>> ok
                  {'five': '5', 'two': '2', 'one': '1'}
                  
                  >>> if ('two' and 'one' and 'five') in ok:
                  ...   print "cool"
                  ... 
                  cool
                  

                  这似乎有效

                  【讨论】:

                  • 这很聪明,我坚信它在我自己尝试之前没有用。我怀疑() 将首先被评估并产生True,然后检查True in ok。这实际上是如何工作的?!
                  • ('two' and 'one' and 'five') 返回 'five',所以它实际上只检查 'five' 是否在 dict 上
                  【解决方案13】:

                  用于 3 个备选方案的简单基准测试平台。

                  输入你自己的 D 和 Q 值

                  
                  >>> from timeit import Timer
                  >>> setup='''from random import randint as R;d=dict((str(R(0,1000000)),R(0,1000000)) for i in range(D));q=dict((str(R(0,1000000)),R(0,1000000)) for i in range(Q));print("looking for %s items in %s"%(len(q),len(d)))'''
                  
                  >>> Timer('set(q) <= set(d)','D=1000000;Q=100;'+setup).timeit(1)
                  looking for 100 items in 632499
                  0.28672504425048828
                  
                  #This one only works for Python3
                  >>> Timer('set(q) <= d.keys()','D=1000000;Q=100;'+setup).timeit(1)
                  looking for 100 items in 632084
                  2.5987625122070312e-05
                  
                  >>> Timer('all(k in d for k in q)','D=1000000;Q=100;'+setup).timeit(1)
                  looking for 100 items in 632219
                  1.1920928955078125e-05
                  

                  【讨论】:

                  • Python 2.7 有d.viewkeys() 来生成set(q) &lt;= d.viewkeys()
                  • Python 2.7.5 也有 d.keys() 方法。
                  • @IvanKharlamov,但在 Python2 中,它不会返回与 set(q) &lt;= ... 兼容的对象
                  • 我的错,你绝对是正确的:它返回TypeError: can only compare to a set。对不起! :))
                  • 对于 Python 2 切换顺序:d.viewkeys() &gt;= set(q)。我来这里是为了弄清楚为什么订单很重要!
                  【解决方案14】:

                  Alex Martelli 的解决方案set(queries) &lt;= set(my_dict) 是最短的代码,但可能不是最快的。假设 Q = len(queries) 和 D = len(my_dict)。

                  这需要 O(Q) + O(D) 来制作这两个集合,然后(希望!)只有 O(min(Q,D)) 来做子集测试——当然假设 Python 集合查找是 O(1) - 这是最坏的情况(当答案为 True 时)。

                  hughdbrown (et al?) all(k in my_dict for k in queries) 的生成器解决方案是最坏情况 O(Q)。

                  复杂因素:
                  (1) 基于集合的小工具中的循环都以 C 速度完成,而基于任意的小工具在字节码上循环。
                  (2) any-based gadget 的调用者可能能够使用任何关于失败概率的知识来相应地对查询项进行排序,而基于 set 的 gadget 不允许这样的控制。

                  与往常一样,如果速度很重要,那么在操作条件下进行基准测试是个好主意。

                  【讨论】:

                  【解决方案15】:
                  >>> if 'foo' in foo and 'bar' in foo:
                  ...     print 'yes'
                  ... 
                  yes
                  

                  Jason, () 在 Python 中不是必需的。

                  【讨论】:

                  • 它们仍然可能是很好的风格......没有它们,我的 C++ 痴迷的大脑总是想知道它是否会被解释为“如果 'foo in (foo and 'bar') in foo:”
                  • 我知道它们不是必需的。我只是觉得他们在这种情况下增加了清晰度。
                  【解决方案16】:

                  使用 sets

                  if set(("foo", "bar")).issubset(foo):
                      #do stuff
                  

                  或者:

                  if set(("foo", "bar")) <= set(foo):
                      #do stuff
                  

                  【讨论】:

                  • 我在回答中使用的 set(d) 就像 set(d.keys()) 但更快、更短,而且我会说在风格上更可取。
                  • set(d)set(d.keys()) 相同(没有d.keys() 构造的中间列表)
                  【解决方案17】:

                  并不是说这不是你没有想到的事情,但我发现最简单的事情通常是最好的:

                  if ("foo" in foo) and ("bar" in foo):
                      # do stuff
                  

                  【讨论】:

                    【解决方案18】:

                    如果你想:

                    • 同时获取键的值
                    • 检查多个字典

                    然后:

                    from operator import itemgetter
                    foo = {'foo':1,'zip':2,'zam':3,'bar':4}
                    keys = ("foo","bar") 
                    getter = itemgetter(*keys) # returns all values
                    try:
                        values = getter(foo)
                    except KeyError:
                        # not both keys exist
                        pass
                    

                    【讨论】:

                      【解决方案19】:

                      使用 lambda 怎么样?

                       if reduce( (lambda x, y: x and foo.has_key(y) ), [ True, "foo", "bar"] ): # do stuff
                      

                      【讨论】:

                      • 这个答案是唯一一个功能正确的答案,它可以通过简单的更改 (s/True/1/) 在 Python 1.5 上运行......但它没有其他任何意义。并且 True 的东西作为可选的初始化器 arg 会更好,而不是塞进序列 arg 的前面。
                      【解决方案20】:

                      好吧,你可以这样做:

                      >>> if all (k in foo for k in ("foo","bar")):
                      ...     print "They're there!"
                      ...
                      They're there!
                      

                      【讨论】:

                      • +1,我比 Greg 的回答更喜欢这个,因为它更简洁、更快(不构建不相关的临时列表,并充分利用短路)。
                      • 我喜欢 all() 和 any()。它们使许多算法变得如此简洁。
                      • 我最终使用了这个解决方案。对于较大的数据集,这似乎是最好的。在检查 25 或 30 个键时。
                      • 由于短路,这是一个很好的解决方案,尤其是在测试经常失败的情况下;除非您可以只创建一次感兴趣的密钥集并多次检查它,在这种情况下set 更好。像往常一样...测量它!-)
                      • checkout @claytonk 的解决方案,更简洁,性能提升 60%
                      猜你喜欢
                      • 1970-01-01
                      • 2022-08-03
                      • 1970-01-01
                      • 2016-12-27
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2015-09-25
                      相关资源
                      最近更新 更多