Python 2.X中range和xrange函数有什么区别?

843

显然xrange更快,但我不知道它为什么更快(并且除了到目前为止只是口头陈述的证据外没有其他证据),也不知道除了这个之外还有什么不同之处

for i in range(0, 20):
for i in xrange(0, 20):
28个回答

1022

在 Python 2.x 中:

  • range 创建一个列表,因此如果您执行 range(1, 10000000),它将在内存中创建一个具有 9999999 个元素的列表。

  • xrange 是一个惰性求值的序列对象。

在 Python 3 中:

  • range 执行 Python 2 中的 xrange 的等价操作。要获取列表,必须明确使用 list(range(...))

  • xrange 不再存在。


41
为什么要创建xrange,而不是使range变成lazy呢? - Rob Grant
2
如果您对该列表进行1000次迭代,每次生成值的速度会变慢。 - Alvaro
30
@RobertGrant,他们确实做到了。这发生在Python 3中。(在Python 2.x系列中,他们无法这样做,因为所有更改都必须向后兼容。) - Paul Draper
10
有人能解释一下"evaluates lazily"是什么意思吗?谢谢! (翻译:Can someone explain what "evaluates lazily" means? Thanks!) - ratulotron
17
这句话的意思是每个 i 都是按需求计算而不是在初始化时计算。 - Onilol
显示剩余3条评论

237

range创建一个列表,所以如果你执行range(1, 10000000),它会在内存中创建一个包含9999999个元素的列表。

xrange是一个生成器,因此它是一个惰性评估的序列对象。

这是正确的,但是在Python 3中,range()将由Python 2的xrange()实现。如果您需要实际生成列表,则需要执行以下操作:

list(range(1,100))

3
我认为这不会成为一个很大的问题(关于破坏现有应用程序),因为range函数主要是用于生成索引,以在for循环中使用,例如 "for i in range(1, 10):"。 - Benjamin Autin
11
谢谢这个答案,提到Python 3用xrange替代range的信息非常有用。我之前告诉别人在Python 3中要用xrange而不是range,但他们说在Python 3中没有区别,所以我就谷歌搜索了更多信息,然后看到了这个答案 :) - Cervo
1
@winterlight,我认为正确的术语应该是迭代器。生成器也应该能够接收。 - McSinyx
1
@scign,有关生成器协议的规范定义,请参阅PEP 342。在type annotation documentation中可以找到一个很好的总结(这些被别名为typing.*)。 - McSinyx
1
不,我没有,@scign。你读过链接的PEP和文档吗?过去这两个术语可能是可以互换使用的,但在撰写本文时,生成器必须能够接收值。此外,Python 3中的range也不是迭代器(尝试next(range(42)))。 - McSinyx
显示剩余3条评论

121

记住,使用 timeit 模块测试哪个小代码片段更快!

$ python -m timeit 'for i in range(1000000):' ' pass'
10 loops, best of 3: 90.5 msec per loop
$ python -m timeit 'for i in xrange(1000000):' ' pass'
10 loops, best of 3: 51.1 msec per loop

就我个人而言,我总是使用 range(),除非我要处理真正的大型列表——因为从时间上来看,对于一百万条目的列表,额外的开销只有 0.04 秒。此外,正如 Corey 指出的那样,在 Python 3.0 中,xrange() 将被淘汰,而 range() 将会提供很好的迭代器行为。


13
时间测量示例加1分。注意:在Windows命令提示符中运行需要使用双引号,即". 因此,代码将是 python -m timeit "for i in xrange(1000000):" " pass" - stalk
15
xrange的主要优点是节省内存,而非时间。 - endolith
3
对于实用性的回答,我给予肯定:除非数据集非常大,使用range函数最好。顺便说一下,它们在概念上是相同的,对吗?奇怪的是没有一个回答明确说明这一点。 - Bob Stein
7
如果xrange更快且不会占用大量内存,那么为什么还要使用range呢? - Austin Mohr
11
我基本上同意您的说法,但是您的评估是错误的:"额外开销仅为0.04秒"不是正确的看法,"(90.5-51.1)/51.1 = 1.771倍慢"才是正确的,因为它传达了如果这是程序的核心循环,它可能会成为瓶颈。然而,如果这只是一个小部分,1.77倍并不算太多。 - chacham15
3
这是错误的建议。实际上,使用range唯一的优点是如果你觉得它比xrange更易读,或者你确实需要一个列表。此外,在您的计算机上运行timeit很可能无法反映您的代码将要运行的环境,并且无法告诉您内存方面的影响。 - TM.

69

xrange仅存储范围参数并根据需要生成数字。但是,Python的C实现目前将其参数限制为C长整型:

xrange(2**32-1, 2**32+1)  # When long is 32 bits, OverflowError: Python int too large to convert to C long
range(2**32-1, 2**32+1)   # OK --> [4294967295L, 4294967296L]

请注意,在Python 3.0中只有range函数,其行为类似于2.x版本的xrange函数,但没有最小和最大结束点的限制。


44

xrange 返回一个迭代器,并且一次只保留一个数字在内存中。range 会在内存中保留整个数字列表。


12
xrange不返回迭代器。 - abarnert
并且一次只保留一个数字在内存中,其他数字放在哪里请指导我。 - SIslam
7
如果已知起始点、终止点和当前点,它可以逐步计算出下一个点。 - Justin Meiners

32

请务必花时间阅读库参考文献,你越熟悉它,就越能快速找到像这样的问题的答案。特别重要的是关于内置对象和类型的前几章。

xrange 类型的优势在于:不管所表示的范围大小,xrange 对象始终需要同样少的内存。但没有一致的性能优势。

另一种查找 Python 构造快速信息的方式是 docstring 和 help 函数:

print xrange.__doc__ # def doc(x): print x.__doc__ is super useful
help(xrange)

1
这个库很好,但是有时候很难得到你所要的答案。 - Teifion
2
去图书馆参考资料,按下ctrl+f键,搜索range,你会得到两个结果。找到这个问题的答案并不需要太多的努力。 - David Locke
1
图书馆参考资料无法使用。您能否更新一下? - Sandeep

19

从文档中

这个函数与range()非常相似,但返回的是一个xrange对象而不是一个列表。这是一种不透明的序列类型,它产生与相应列表相同的值,而不会同时存储它们所有的值。 xrange()相对于range()的优势很小(因为xrange()仍然需要在被要求时创建值),除非在内存不足的机器上使用非常大的范围或者当范围的所有元素都没有被使用时(例如当循环通常以break终止时)。


15

在这个简单的例子中,你会发现xrange相较于range有更多的优势:

import timeit

t1 = timeit.default_timer()
a = 0
for i in xrange(1, 100000000):
    pass
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 4.49153590202 seconds

t1 = timeit.default_timer()
a = 0
for i in range(1, 100000000):
    pass
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 7.04547905922 seconds

上面的例子并没有在 xrange 的情况下反映出任何实质性的改进。

现在看看下面这个例子,在这个例子中,xrange 相比之下真的非常慢。

import timeit

t1 = timeit.default_timer()
a = 0
for i in xrange(1, 100000000):
    if i == 10000:
        break
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 0.000764846801758 seconds

t1 = timeit.default_timer()
a = 0
for i in range(1, 100000000):
    if i == 10000:
        break
t2 = timeit.default_timer() 

print "time taken: ", (t2-t1)  # 2.78506207466 seconds

使用range函数,它会创建一个从0到100000000的列表(耗时较长),但是xrange是一个生成器,只会根据需求生成数字,也就是说,如果迭代继续进行。

在Python-3中,range的实现方式与Python-2中的xrange相同,而他们已经在Python-3中取消了xrange

Happy Coding!


14

range 函数创建一个列表,所以如果你执行 range(1, 10000000),它会在内存中生成一个包含 10000000 个元素的列表。 xrange 是一个生成器,它会惰性地进行计算。

这带来了两个优点:

  1. 可以遍历更长的列表而不会出现 MemoryError
  2. 由于它是惰性求值,因此如果您提前停止迭代,您将不会浪费时间创建整个列表。

12

这是出于优化的原因。

在你的例子中,range()会创建从开始到结束的值列表(0..20)。对于非常大的范围,这将成为一项昂贵的操作。

另一方面,xrange()更加优化。它只有在需要时才计算下一个值(通过xrange序列对象),不像range()那样创建所有值的列表。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接