据我了解, range()
函数实际上 是 Python 3 中的一种对象类型,它动态生成其内容,类似于生成器。
在这种情况下,我预计以下行会花费过多的时间,因为为了确定 1 万亿是否在该范围内,必须生成 1 万亿值:
1_000_000_000_000_000 in range(1_000_000_000_000_001)
此外:似乎无论我添加多少个零,计算或多或少都需要相同的时间(基本上是瞬时的)。
我也尝试过这样的事情,但计算仍然几乎是即时的:
# count by tens
1_000_000_000_000_000_000_000 in range(0,1_000_000_000_000_000_000_001,10)
如果我尝试实现我自己的范围函数,结果不是那么好!
def my_crappy_range(N):
i = 0
while i < N:
yield i
i += 1
return
range()
对象在幕后做了什么让它这么快?
选择 Martijn Pieters 的答案 是因为它的完整性,但也请参阅 abarnert 的第一个答案,以很好地讨论 range
在 Python 3 中成为一个成熟的 _序列_,以及一些关于潜在不一致的信息/警告对于 __contains__
跨 Python 实现的函数优化。 abarnert 的另一个答案 更详细,并为那些对 Python 3 中优化背后的历史感兴趣的人提供了链接(以及 Python 2 中缺乏 xrange
的优化)。 poke 和 wim 的回答为感兴趣的人提供了相关的 C 源代码和解释。
原文由 Rick supports Monica 发布,翻译遵循 CC BY-SA 4.0 许可协议
Python 3
range()
对象不会立即产生数字;它是一个 按需 生成数字的智能 序列对象。它只包含您的开始、停止和步长值,然后当您迭代对象时,每次迭代都会计算下一个整数。该对象还实现了
object.__contains__
挂钩,并 计算 您的数字是否在其范围内。计算是(接近)恒定时间操作* 。永远不需要扫描范围内所有可能的整数。来自
range()
对象文档:所以至少,你的
range()
对象会做:这仍然缺少真正的
range()
支持的几件事(例如.index()
或.count()
方法,但应该给出哈希、相等测试或切片)你有个主意。我还简化了
__contains__
实现只关注整数测试;如果您给一个真正的range()
对象一个非整数值(包括int
的子类),则会启动慢速扫描以查看是否存在匹配,就像您使用针对所有包含值的列表进行包含测试。这样做是为了继续支持其他恰好支持整数相等测试但预计也不支持整数算术的数字类型。请参阅实现包含测试的原始 Python 问题。* 接近 恒定时间,因为 Python 整数是无界的,所以数学运算也会随着 N 的增长而增长,这使得它成为 O(log N) 运算。由于它全部在优化的 C 代码中执行,并且 Python 将整数值存储在 30 位块中,因此由于此处涉及的整数的大小,您会在看到任何性能影响之前耗尽内存。