【问题标题】:why 'in' operator with tuple as a key in python so slow?为什么以元组为键的'in'运算符在python中这么慢?
【发布时间】:2012-04-29 15:54:10
【问题描述】:

我有一个字典,例如:

d=dict()
d[('1','2')] = 'value'

然后我查询密钥:

if (k1,k2) in d.keys():

当有百万条记录时,速度很痛苦,'in'操作符有什么问题吗?

是顺序搜索吗​​?

我必须连接 str 作为绕过此问题的关键。

【问题讨论】:

  • 你能告诉我们你用来得出这个结论的代码吗?
  • 很有趣的问题,我研究一下
  • 您的编辑引入了一个全新的问题:您为什么不在单独的问题中提出呢?我认为这更有意义。
  • 我将问题转回原来的形式。如果您想提出不同的问题,请在新问题中提出。
  • 对不起,我是stackoverflow的新手。最后的编辑是显示代码。

标签: python dictionary key


【解决方案1】:

你应该使用

(k1,k2) in d

而不是调用d.keys()

按照自己的方式进行操作,在 Python 2 中会导致线性搜索,并且会否定 dict 的好处。在 Python 3 中,您的代码是高效的(参见下面的 cmets),但我的版本更清晰。

【讨论】:

  • 这对 Python 2 和 3 都适用吗?我以为.keys() 会在 Python 3 中提供视图对象,而不是列表?
  • 我很难理解为什么有人会使用d.keys()。在 Python 2.x 中,这将每次创建一个包含一百万条记录的列表。即使在 3.x 中,它仍然是完全多余的。
  • @TimPietzcker 即使它创建了一个可迭代的视图对象,in 运算符也必须执行线性搜索。您需要将in 运算符应用于dict 对象。
  • @DavidHeffernan 键的 dict 视图类似于集合,因此它与 dict 本身具有相同的性能。
  • @DavidHeffernan Take a read - 它们实际上是 Python 3 中非常灵活的东西。
【解决方案2】:

考虑到 Nolen Royalty 的添加,我想我会注意您实际上可以以稍微更好的方式进行 timeit 测试。通过将dict 的构造移到设置函数中,我们可以只对dict 上的操作进行计时,从而得到一个可以轻松比较的结果。

在 3.2 中:

python -m timeit -s 'd = {(str(i), str(j)):"a" for i in range(100) for j in range(1000)}' '_ = ("1", "2") in d.keys()' '_ = (1, 2) in d.keys()'
1000000 loops, best of 3: 0.404 usec per loop

python -m timeit -s 'd = {(str(i), str(j)):"a" for i in range(100) for j in range(1000)}' '_ = ("1", "2") in d' '_ = (1, 2) in d'
1000000 loops, best of 3: 0.247 usec per loop

您可以看到差异。在 3.x 中,直接在 dict 上工作可以使我们的速度提高近 2 倍,这还不错。

在 2.7.3 中:

python2 -m timeit -s 'd = {(str(i), str(j)):"a" for i in range(100) for j in range(1000)}' '_ = ("1", "2") in d.keys(); _ = (1, 2) in d.keys()'
10 loops, best of 3: 36.3 msec per loop

python2 -m timeit -s 'd = {(str(i), str(j)):"a" for i in range(100) for j in range(1000)}' '_ = ("1", "2") in d' '_ = (1, 2) in d'
10000000 loops, best of 3: 0.197 usec per loop

在 2.x 中,差异确实惊人。使用 dict.keys() 需要 36,300 微秒,而仅使用 dict 需要不到 0.2 微秒。这快了将近 20 万 倍。

只是觉得这值得一提。

编辑:

Tim 做了一个有趣的评论,所以我决定做另一个测试。我尝试只构建列表,然后只进行哈希查找,结果如下:

python2 -m timeit -s 'd = {(str(i), str(j)):"a" for i in range(100) for j in range(1000)}' 'd.keys()' 'd.keys()'
100 loops, best of 3: 5.84 msec per loop

python2 -m timeit -s 'd = {(str(i), str(j)):"a" for i in range(100) for j in range(1000)}' -s 'l=d.keys()' '_ = ("1", "2") in l' '_ = ("1", "2") in l'
10 loops, best of 3: 25.3 msec per loop

您可以看到,在这样的大字典上,构建列表大约需要 1/6 的时间,而在列表中搜索则需要 5/6 的时间。

【讨论】:

  • 太棒了 - 我很确定这种巨大差异不是主要是因为线性搜索与散列查找,而是因为为每个关键字构建了一个键列表迭代,正如您在对@DavidHeffernan 的回答的评论中所建议的那样。
  • 非常感谢您的提示,我不知道您可以轻松传递这样的设置函数。 +1
  • @NolenRoyalty 这真的很有用——你也可以通过多次使用标志来进行多次设置。您还可以通过放置更多带引号的字符串来避免使用分号。让复杂的测试变得更容易。
  • @TimPietzcker 实际上,我只是做了一个检查并编辑了我的答案以添加到我的结果中——大部分时间都花在了搜索上。我必须承认,我认为创建列表将是最大的因素。 (话说回来,我也不认为差异会这么如此巨大)。
  • @Lattyware:哇。太好了,感谢您深入了解这一点。
猜你喜欢
  • 2018-02-24
  • 2021-01-05
  • 1970-01-01
  • 1970-01-01
  • 2021-01-16
  • 2011-01-13
  • 1970-01-01
  • 2018-10-08
相关资源
最近更新 更多