TL;DR - 问题 21118
长篇故事
Josh Rosenberg 发现 str.translate()
函数比 bytes.translate()
函数慢得多,他提出了一个问题,指出:
在 Python 3 中,str.translate()
通常是性能降低而非优化。
str.translate()
为什么慢?
str.translate()
函数之所以非常慢,主要原因是查找过程以前是在 Python 字典中进行的。
maketrans
的使用使问题变得更糟。使用类似bytes
的方法构建了一个包含 256 个项的 C 数组,以便进行快速表查找。因此,使用较高级别的 Python dict
使得 Python 3.4 中的 str.translate()
非常慢。
现在发生了什么?
第一种方法是添加一个小补丁,translate_writer,但速度提升并不理想。很快就测试了另一个补丁 fast_translate,它产生了非常好的结果,速度提高了多达 55%。
从文件中可以看出主要的更改是将 Python 字典查找改为 C 级别的查找。
现在速度几乎与 bytes
相同。
unpatched patched
str.translate 4.55125927699919 0.7898181750006188
str.translate from bytes trans 1.8910855210015143 0.779950579000797
需要注意的是,性能增强仅在ASCII字符串中才明显。正如J.F.Sebastian在下面的评论中提到的,在Python 3.5之前,translate在ASCII和非ASCII情况下的工作方式是相同的。但是,从Python 3.5开始,ASCII情况要快得多。
以前,ASCII和非ASCII几乎相同,但现在我们可以看到性能有了很大改善。
这可以从这个回答中看到,性能的提升从71.6μs到2.33μs。
以下代码演示了这一点
python3.5 -m timeit -s "text = 'mJssissippi'*100; d=dict(J='i')" "text.translate(d)"
100000 loops, best of 3: 2.3 usec per loop
python3.5 -m timeit -s "text = 'm\U0001F602ssissippi'*100; d={'\U0001F602': 'i'}" "text.translate(d)"
10000 loops, best of 3: 117 usec per loop
python3 -m timeit -s "text = 'm\U0001F602ssissippi'*100; d={'\U0001F602': 'i'}" "text.translate(d)"
10000 loops, best of 3: 91.2 usec per loop
python3 -m timeit -s "text = 'mJssissippi'*100; d=dict(J='i')" "text.translate(d)"
10000 loops, best of 3: 101 usec per loop
结果的表格:
Python 3.4 Python 3.5
Ascii 91.2 2.3
Unicode 101 117
dict.fromkeys(ord(c) for c in '@#$')
。这将为特定字符创建字典键,而不是像以前那样在循环中每次创建一个新的字典。 - Thomas K