【问题标题】:Is it always faster to use string as key in a dict?在字典中使用字符串作为键总是更快吗?
【发布时间】:2012-06-22 18:38:19
【问题描述】:

在这个page,我看到了一些有趣的东西:

请注意,(在实践中)只处理 str 键的 dicts 有一条快速路径;这不会影响算法的复杂性,但会显着影响常数因素:典型程序完成的速度。

那到底是什么意思呢?

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

如果是,为什么?

更新:

感谢关于优化的建议!但实际上我更感兴趣的是显而易见的事实,而不是我们是否应该或何时应该进行优化。

更新 2:

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

" ...

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

... "

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

【问题讨论】:

  • 我猜这一切都归结为关键对象的__hash__ 方法有多快。我猜对字符串进行哈希处理相当简单,但我会对字典查找的多少比例用于哈希处理非常感兴趣。
  • 您的更新不会改变任何东西。不,在大多数情况下它不会更快,除非您的键首先是字符串。
  • @Lattyware 链接页面似乎意味着速度的提高每次查找不仅仅是为了构建。
  • @Wilduck 我不是在谈论建筑。如果您的键一开始不是字符串,那么每次您需要进行查找时,您都需要将您的键转换为字符串。 (除非你用字典做一些琐碎的事情)。
  • @Lattyware 啊,我明白了。虽然我仍然可以想象一些情况,您可以从字符串的前期转换中获得改进。

标签: python


【解决方案1】:

作为 Python dict 基础的 C 代码针对字符串键进行了优化。 You can read about this here(以及博客所指的书中)。

如果 Python 运行时知道你的 dict 只包含字符串键,它可以做一些事情,例如不处理字符串到字符串比较不会发生的错误,并忽略丰富的比较运算符。这将使字符串键的常见情况仅dict 快一点。 (更新:时间显示它不止一点。)

但是,这不太可能对大多数 Python 程序的运行时间产生重大影响。如果您已经测量并发现dict 查找是您代码中的瓶颈,则只需担心此优化。 As the famous quote says, "Premature optimization is the root of all evil."

查看事物真正速度的唯一方法是对它们进行计时:

>>> 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%,我不得不承认我对差异的大小感到惊讶。

【讨论】:

  • 您的测试假定获得"500"500 相比是免费的——这有很大的不同——请参阅我的答案。
  • 问题询问字符串键是否总是更快,我的测试旨在显示,它确实如此。我不认为问题是关于从另一个对象转换为字符串并将其用作键 - 由于多种原因这将是不好的 - 而是简单地说,如果选择可用,是否总是值得使用字符串。
  • 那是断章取义。如果任何获得字符串键的方法会使其变慢,那么知道使用字符串键会更快是没有用的。
  • 我不认为您的示例证明“任何获得字符串键的方式”都很慢。一个浮现在脑海中的例子是具有唯一员工 ID 字符串属性的员工对象;肯定是一个人为的例子,但它说明了一点。就像我说的,如果你在做这种优化,你无论如何都会测量和比较。
【解决方案2】:

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

这确实意味着,如果您有以字符串作为键的小型字典,Python 会很快 - 这是一种常见用法,因此已针对它进行了优化。

正如 Ignacio Vazquez-Abrams 指出的那样,将您的密钥转换为字符串的成本可能(远远)超过您从它作为 dict 的字符串可能获得的轻微提升。

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

一些测试:

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

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

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

【讨论】:

  • 特别是因为将某些类型转换为字符串可能比首先将它们用作键更昂贵。
  • 对不起,我想我应该修改我的问题
  • @IgnacioVazquez-Abrams 非常正确。
  • +1 表示“使用与您的情况相关的内容”。话虽如此,构建一个仅使用字符串键显示显着改进的情况将非常有趣。情况需要有多极端?
  • @Wilduck 正如我所说,它会在最初的键是字符串的小型字典中显示出最大的收益。在任何较大的字典中,可变时间因子将抹去这给您带来的恒定增益。如果您要转换为字符串,那将抵消任何收益。
猜你喜欢
  • 2017-12-18
  • 2015-09-16
  • 2021-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-16
  • 1970-01-01
  • 2019-03-17
相关资源
最近更新 更多