为什么 for _ in range(n) 比 for _ in [""]*n 慢?

Why is for _ in range(n) slower than for _ in [""]*n?

测试 for _ in range(n) 的替代方案(执行某个动作 n 次,即使该动作不依赖于 n 的值)我注意到还有另一种表述这种模式更快,for _ in [""] * n.

例如:

timeit('for _ in range(10^1000): pass', number=1000000)

returns 16.4 秒;

timeit('for _ in [""]*(10^1000): pass', number=1000000)

需要 10.7 秒。

为什么 [""] * 10^1000 在 Python 3 中比 range(10^1000) 快这么多?

所有测试使用 Python 3.3

您的问题是喂食不正确 timeit

您需要提供 timeit 个包含 Python 个语句的字符串。如果你这样做

stmt = 'for _ in ['']*100: pass'

查看stmt的值。方括号内的引号字符与字符串定界符匹配,因此它们被 Python 解释为字符串定界符。由于 Python 连接相邻的字符串文字,您将看到您真正拥有的与 'for _ in [' + ']*100: pass' 相同,这给您 'for _ in []*100: pass'.

所以你的 "super-fast" 循环只是在空列表上循环,而不是 100 个元素的列表。试试你的测试,例如,

stmt = 'for _ in [""]*100: pass'

当遍历 range() 时,生成 0 到 n 之间所有整数的对象;这需要(少量)时间,即使 small integers having been cached.

另一方面,[None] * n 上的循环会生成对 1 个对象的 n 引用,并且创建该列表的速度要快一些。

然而,range() 对象使用 far 更少的内存, 启动时更具可读性,这就是为什么人们更喜欢使用它。大多数代码不必从性能中榨取最后一滴。

如果您需要这样的速度,您可以使用不占用内存的自定义迭代器,使用 itertools.repeat() 和第二个参数:

from itertools import repeat

for _ in repeat(None, n):

至于你的计时测试,有一些问题。

首先,你的['']*n计时循环出错了;您没有嵌入两个引号,而是连接了两个字符串并生成了一个 空列表 :

>>> '['']*n'
'[]*n'
>>> []*100
[]

这在迭代中将是无与伦比的,因为您迭代了 0 次。

你也没有用大数字; ^是二元异或运算符,不是幂运算符:

>>> 10^1000
994

这意味着您的测试错过了创建空值列表所需的时间。

使用更好的数字和 None 给你:

>>> from timeit import timeit
>>> 10 ** 6
1000000
>>> timeit("for _ in range(10 ** 6): pass", number=100)
3.0651066239806823
>>> timeit("for _ in [None] * (10 ** 6): pass", number=100)
1.9346517859958112
>>> timeit("for _ in repeat(None, 10 ** 6): pass", 'from itertools import repeat', number=100)
1.4315521717071533