为什么'键入d.keys()'在O(n)时完成,而'key in d'在O(1)中完成?

时间:2014-07-02 21:03:02

标签: python dictionary

我有this one的后续问题。原始问题的一位评论者提到他过去曾经误认为人们使用的语法如下:

key in d.keys()

在O(n)时间结束,而不是

key in d

在O(1)时间内完成,没有意识到差异。直到今天(当我在试图理解为什么我的代码运行如此缓慢之后偶然发现原始问题时),我才是其中之一。我试图使用Python 2.7.5验证评论的准确性,果然,这是timeit的结果:

$ python -m timeit -s 'd=dict.fromkeys(range(100))' '1000 in d.keys()'
100000 loops, best of 3: 2.18 usec per loop
$ python -m timeit -s 'd=dict.fromkeys(range(100))' '1000 in d'
10000000 loops, best of 3: 0.0449 usec per loop
$ python -m timeit -s 'd=dict.fromkeys(range(1000))' '10000 in d.keys()'
100000 loops, best of 3: 17.9 usec per loop
$ python -m timeit -s 'd=dict.fromkeys(range(1000))' '10000 in d'
10000000 loops, best of 3: 0.0453 usec per loop    

对于具有100个密钥的字典,速度差异大约为50倍(2.19 usec / 0.0449 usec),对于具有1000个密钥的字典,存在400X差异(17.9 usec / 0.0453 usec),对于明确的搜索构造使得搜索关键字太大而无法在字典中找到。换句话说,O(n)与O(1),正如评论者所说的那样。

我的问题:为什么会有所不同?这两种语法看起来几乎相同!显然,Python必须在这两种情况之间做一些非常不同的事情,但究竟在内部发生的事实上会产生区别呢?

2 个答案:

答案 0 :(得分:8)

d.keys()返回一个列表,它是dict键的副本,而不是视图。构造该列表需要O(n),查找也是如此,它使用list.__contains__,即迭代键。

另一方面,key in d基本上调用

d.__contains__(key)

使用dict键上的O(1)散列查找有效地实现方法dict.__contains__。这正是dict数据结构的 raison d'être,当您使用{{1}访问dict时,获得快速O(1)查找的原因相同}。

总之,d[key]在python 2中永远不合适。

注意:在python3中已经改变了,其中使用key in d.keys()或多或少等同于key in d.keys()对于python 2。

答案 1 :(得分:0)

字典使用key的散列来索引键。如果索引存在,则只需进行一次比较;那是O(1)。 如果您使用d.keys(),您首先会获得所有密钥的副本作为列表,然后您必须将此列表的每个元素与您的key进行比较,这就是为什么它是O(n)。