使用字符串作为字典中的键总是更快吗?

问题描述 投票:0回答:2

在这个页面,我看到一些有趣的东西:

请注意,(实际上)有一个仅处理 str 键的字典的快速路径;这不会影响算法的复杂性,但会显着影响常量因素:典型程序完成的速度。

那么它到底意味着什么呢?

这是否意味着使用字符串作为键总是更快?

如果是,为什么?

更新:

感谢您的优化建议!但我实际上对简单的事实更感兴趣,而不是我们是否或何时应该进行优化。

更新2:

感谢您的精彩回答,我将在这里引用@DaveWebb提供的link中的内容:

” ...

ma_lookup最初设置为lookdict_string函数(在3.0中重命名为lookdict_unicode),它假设字典中的键和正在搜索的键都是标准PyStringObject的。然后,它能够进行一些优化,例如减轻各种错误检查,因为字符串与字符串的比较永远不会引发异常。也不需要丰富的对象比较,这意味着我们避免调用 PyObject_RichCompareBool,而始终直接使用 _PyString_Eq

... ”

另外,对于实验数字,我认为如果没有int到string的转换,差异的大小会更大

python
2个回答
25
投票

Python 字典底层的 C 代码针对字符串键进行了优化。 您可以在此处阅读相关内容(以及博客引用的书中)。

如果Python运行时知道你的字典只包含字符串键,它可以做一些事情,例如不处理字符串与字符串比较不会发生的错误,并忽略丰富的比较运算符。这将使字符串键的常见情况仅

dict
更快一些。 (更新:时间显示它不止一点点。)

但是,这不太可能对大多数 Python 程序的运行时间产生重大改变。仅当您测量并发现

dict
查找是代码中的瓶颈时才需要担心此优化。 正如名言所说,“过早的优化是万恶之源。”

了解事物到底快了多少的唯一方法就是为它们计时:

>>> timeit.timeit('a["500"]','a ={}\nfor i in range(1000): a[str(i)] = i')
0.06659698486328125
>>> timeit.timeit('a[500]','a ={}\nfor i in range(1000): a[i] = i')
0.09005999565124512

因此,使用字符串键甚至比

int
键快大约 30%,我不得不承认我对差异的大小感到惊讶。


11
投票

由于这仅影响恒定时间,因此可能根本不重要。您真正需要优化的唯一时间是当您处理非常大的数据集时 - 这不会产生任何影响。

这意味着,如果你有一个以字符串为键的小字典,Python 会很快 - 这是一种常见用法,所以它已经过优化。

正如 Ignacio Vazquez-Abrams 指出的那样,将密钥转换为字符串的成本可能(远远)高于将其作为字典的字符串可能获得的轻微提升。

简而言之,使用与您的情况相关的内容 - 优化应该只在需要时进行,而不是之前。

一些测试:

python -m timeit -s "a={key: 1 for key in range(1000)}" "a[500]"
10000000 loops, best of 3: 0.0773 usec per loop

python -m timeit -s "a={str(key): 1 for key in range(1000)}" "a[\"500\"]"
10000000 loops, best of 3: 0.0452 usec per loop

python -m timeit -s "a={str(key): 1 for key in range(1000)}" "a[str(500)]"
1000000 loops, best of 3: 0.244 usec per loop

如您所见,虽然基于字符串的字典速度更快,但相比之下转换密钥的成本非常昂贵,完全减轻了增益(然后是一些)。

所以是的,如果您使用的数据用作字典的键,并且以什么格式存储它们并不重要,那么在小字典中使用字符串是更好的选择。实际上,这是一种非常罕见的情况(您可能已经在使用字符串了)。

© www.soinside.com 2019 - 2024. All rights reserved.