为什么enumerate这么慢?
这个问题以前也被问过 asked before,但我没有看到关于在现代Python中为何会是这样的合理解释。
我已经搭好了一个测试装置来试图证明这一点。
这里有两个函数。每个函数都达到完全相同的目标(它实际在做什么并不重要)。一个正确地使用了enumerate(并且可能被认为更符合 pythonic 的风格),另一个使用一个离散变量进行手动修改。否则功能完全相同。
对要执行的函数进行伪随机选择可能显得有点“过头”,但我是在尝试消除任何潜在的Python内部缓存(如果确实存在这样的东西的话)。
使用enumerate的那个函数的运行速度始终比另一个慢大约7.5%。
下面是代码:
from math import comb
from timeit import timeit
from random import choice
from collections import defaultdict
def pdiff(v1: float, v2: float) -> float:
return (v1 - v2) / ((v1 + v2) / 2) * 100.0
def func1(s: str) -> bool:
digits = [int(d) for d in s]
n2 = len(s) - 2
a = b = i = 0
for d1, d2 in zip(digits, digits[1:]):
c = comb(n2, i)
a += c * d1
b += c * d2
i += 1
return a % 10 == b % 10
def func2(s: str) -> bool:
digits = [int(d) for d in s]
n2 = len(s) - 2
a = b = 0
for i, (d1, d2) in enumerate(zip(digits, digits[1:]), 0):
c = comb(n2, i)
a += c * d1
b += c * d2
return a % 10 == b % 10
if __name__ == "__main__":
data = "1234567890"
assert func1(data) == func2(data)
functions = (func1, func2)
analysis = defaultdict(list)
for _ in range(50):
function = choice(functions)
duration = timeit(lambda: function(data), number=250_000)
name = function.__name__
analysis[name].append(duration)
durations = []
for k, v in analysis.items():
average = sum(v) / len(v)
print(f"{k} {average:.4f}s")
durations.append(average)
assert len(durations) == 2
print(f"{pdiff(*durations):.2f}%")
平台:
- Apple Silicon(M2)
- MacOS 26.3.1
- Python 3.14.3
解决方案
你看到的并不是 enumerate 出了什么“错误”,只是这两个循环结构安排的副作用。
在你的第二个版本中,每次迭代都要经过额外的一层:zip 产生一个对,然后 enumerate 将其包裹成带索引的另一个元组,接着Python必须解包这个嵌套的结构。这比简单地让局部变量自增多了一些工作。
在第一版中,i 只是一个普通的局部变量,而 i += 1 已经尽可能地便宜了。因此在紧密循环中,结果略微快一些也就不足为奇。
现在,关于基准测试本身——对于这么小的量级,它并不理想:
- 你是在对一个非常短的输入进行循环,因此微小的差异会被放大。
- 循环体执行的是一个相对昂贵的操作(
comb),这让你更难分离出你真正想要衡量的部分。 - 随机选择要运行的函数并没有真正的帮助,反而增加了噪声。
- 把调用包装在lambda里面也会带来额外的开销。
所以,是的,在这里看到大约7% 的差异是合理的,但这并不意味着 enumerate 在一般情况下“慢”。它只是意味着在这个特定的微观场景中,手动计数器更便宜一点。
如果你关心这里的性能,最大的收益不是替换 enumerate,而是在每次迭代中避免重新计算组合。这才是真正的成本所在。
至于你的观点:我之所以提CPython,是因为它是大多数人所使用的参考解释器,但原理并不依赖于它——关键是每次迭代额外做的工作量。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。