为什么或者为什么不?
12 回答
对于性能,尤其是在大范围迭代时,xrange()
通常会更好。但是,在某些情况下您可能更喜欢range()
:
在 python 3 中,
range()
做了xrange()
以前做的事情,现在xrange()
不存在了。如果您想编写可在 Python 2 和 Python 3 上运行的代码,则不能使用xrange()
.range()
在某些情况下实际上可以更快 - 例如。如果多次迭代相同的序列。xrange()
每次都必须重建整数对象,但range()
会有真正的整数对象。(然而,它在内存方面的表现总是更差)xrange()
并非在需要真实列表的所有情况下都可用。例如,它不支持切片或任何列表方法。
[编辑] 有几篇文章提到range()
了 2to3 工具将如何升级。作为记录,这是在一些示例用法上运行该工具的输出range()
和xrange()
RefactoringTool: Skipping implicit fixer: buffer
RefactoringTool: Skipping implicit fixer: idioms
RefactoringTool: Skipping implicit fixer: ws_comma
--- range_test.py (original)
+++ range_test.py (refactored)
@@ -1,7 +1,7 @@
for x in range(20):
- a=range(20)
+ a=list(range(20))
b=list(range(20))
c=[x for x in range(20)]
d=(x for x in range(20))
- e=xrange(20)
+ e=range(20)
如您所见,当在 for 循环或理解中使用时,或者已经用 list() 包装时,范围保持不变。
不,它们都有各自的用途:
迭代时使用xrange()
,因为它可以节省内存。说:
for x in xrange(1, one_zillion):
而不是:
for x in range(1, one_zillion):
另一方面,range()
如果您确实想要一个数字列表,请使用。
multiples_of_seven = range(7,100,7)
print "Multiples of seven < 100: ", multiples_of_seven
仅当您需要实际列表时才应优先range()
考虑。xrange()
例如,当您想要修改由 返回的列表range()
时,或者当您想要对其进行切片时。对于迭代甚至只是普通的索引,都可以正常xrange()
工作(通常效率更高)。有一点range()
比xrange()
非常小的列表要快一点,但是根据您的硬件和各种其他细节,收支平衡可能是长度为 1 或 2 的结果;没什么好担心的。更喜欢xrange()
.
另一个区别是 Python 2 的实现xrange()
不支持大于 C 整数的数字,所以如果你想使用 Python 内置的大数支持来获得一个范围,你必须使用range()
.
Python 2.7.3 (default, Jul 13 2012, 22:29:01)
[GCC 4.7.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> range(123456787676676767676676,123456787676676767676679)
[123456787676676767676676L, 123456787676676767676677L, 123456787676676767676678L]
>>> xrange(123456787676676767676676,123456787676676767676679)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: Python int too large to convert to C long
Python 3 没有这个问题:
Python 3.2.3 (default, Jul 14 2012, 01:01:48)
[GCC 4.7.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> range(123456787676676767676676,123456787676676767676679)
range(123456787676676767676676, 123456787676676767676679)
xrange()
效率更高,因为它不是生成对象列表,而是一次只生成一个对象。而不是 100 个整数,以及它们的所有开销,以及将它们放入的列表,您一次只有一个整数。更快的生成,更好的内存使用,更高效的代码。
除非我特别需要某事的清单,否则我总是赞成xrange()
range() 返回一个列表,xrange() 返回一个 xrange 对象。
xrange() 速度更快,内存效率更高。但收益并不是很大。
列表使用的额外内存当然不仅仅是浪费,列表具有更多功能(切片、重复、插入……)。可以在文档中找到确切的差异。没有严格的规则,使用需要的东西。
Python 3.0 仍在开发中,但 IIRC range() 将与 2.X 的 xrange() 非常相似,并且 list(range()) 可用于生成列表。
我只想说,获得具有切片和索引功能的 xrange 对象并不难。我已经编写了一些运行良好的代码,并且在计算(迭代)时与 xrange 一样快。
from __future__ import division
def read_xrange(xrange_object):
# returns the xrange object's start, stop, and step
start = xrange_object[0]
if len(xrange_object) > 1:
step = xrange_object[1] - xrange_object[0]
else:
step = 1
stop = xrange_object[-1] + step
return start, stop, step
class Xrange(object):
''' creates an xrange-like object that supports slicing and indexing.
ex: a = Xrange(20)
a.index(10)
will work
Also a[:5]
will return another Xrange object with the specified attributes
Also allows for the conversion from an existing xrange object
'''
def __init__(self, *inputs):
# allow inputs of xrange objects
if len(inputs) == 1:
test, = inputs
if type(test) == xrange:
self.xrange = test
self.start, self.stop, self.step = read_xrange(test)
return
# or create one from start, stop, step
self.start, self.step = 0, None
if len(inputs) == 1:
self.stop, = inputs
elif len(inputs) == 2:
self.start, self.stop = inputs
elif len(inputs) == 3:
self.start, self.stop, self.step = inputs
else:
raise ValueError(inputs)
self.xrange = xrange(self.start, self.stop, self.step)
def __iter__(self):
return iter(self.xrange)
def __getitem__(self, item):
if type(item) is int:
if item < 0:
item += len(self)
return self.xrange[item]
if type(item) is slice:
# get the indexes, and then convert to the number
start, stop, step = item.start, item.stop, item.step
start = start if start != None else 0 # convert start = None to start = 0
if start < 0:
start += start
start = self[start]
if start < 0: raise IndexError(item)
step = (self.step if self.step != None else 1) * (step if step != None else 1)
stop = stop if stop is not None else self.xrange[-1]
if stop < 0:
stop += stop
stop = self[stop]
stop = stop
if stop > self.stop:
raise IndexError
if start < self.start:
raise IndexError
return Xrange(start, stop, step)
def index(self, value):
error = ValueError('object.index({0}): {0} not in object'.format(value))
index = (value - self.start)/self.step
if index % 1 != 0:
raise error
index = int(index)
try:
self.xrange[index]
except (IndexError, TypeError):
raise error
return index
def __len__(self):
return len(self.xrange)
老实说,我认为整个问题有点愚蠢,无论如何 xrange 都应该做这一切......
出于以下原因选择范围:
1) xrange 将在较新的 Python 版本中消失。这为您提供了轻松的未来兼容性。
2) range 将具有与 xrange 相关的效率。
书中给出的一个很好的例子:Magnus Lie Hetland的Practical Python
>>> zip(range(5), xrange(100000000))
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]
我不建议在前面的示例中使用 range 而不是 xrange ——尽管只需要前五个数字,但 range 会计算所有数字,这可能需要很长时间。使用 xrange,这不是问题,因为它只计算所需的数字。
是的,我阅读了@Brian 的回答:在 python 3 中,range() 无论如何都是一个生成器,并且 xrange() 不存在。
好的,对于 xrange 与 range 的权衡和优势,这里的每个人都有不同的看法。它们大多是正确的, xrange 是一个迭代器,并且 range 充实并创建了一个实际列表。在大多数情况下,您不会真正注意到两者之间的区别。(您可以将 map 与 range 一起使用,但不能与 xrange 一起使用,但它会占用更多内存。)
但是,我认为您希望听到的是首选选择是 xrange。由于 Python 3 中的 range 是一个迭代器,因此代码转换工具 2to3 将正确地将所有 xrange 的使用转换为 range,并且会针对 range 的使用抛出错误或警告。如果您想确保将来轻松转换代码,您将仅使用 xrange,当您确定需要列表时使用 list(xrange)。我在今年(2008 年)在芝加哥的 PyCon 的 CPython 冲刺中了解到这一点。
range()
:range(1, 10)
返回一个 1 到 10 个数字的列表并将整个列表保存在内存中。xrange()
: 与 类似range()
,但不是返回列表,而是返回一个对象,该对象根据需要生成范围内的数字。对于循环,这比range()
内存效率要快一些。xrange()
像迭代器一样的对象并按需生成数字(惰性评估)。
In [1]: range(1,10)
Out[1]: [1, 2, 3, 4, 5, 6, 7, 8, 9]
In [2]: xrange(10)
Out[2]: xrange(10)
In [3]: print xrange.__doc__
Out[3]: xrange([start,] stop[, step]) -> xrange object
range()
执行与 Python 3中相同的xrange()
操作,并且 Python 3 中不xrange()
存在
术语。range()
如果您多次迭代相同的序列,在某些情况下实际上可以更快。xrange()
每次都必须重建整数对象,但range()
会有真正的整数对象。
虽然xrange
比range
大多数情况下要快,但性能差异非常小。range
下面的小程序比较了对 a和 an 的迭代xrange
:
import timeit
# Try various list sizes.
for list_len in [1, 10, 100, 1000, 10000, 100000, 1000000]:
# Time doing a range and an xrange.
rtime = timeit.timeit('a=0;\nfor n in range(%d): a += n'%list_len, number=1000)
xrtime = timeit.timeit('a=0;\nfor n in xrange(%d): a += n'%list_len, number=1000)
# Print the result
print "Loop list of len %d: range=%.4f, xrange=%.4f"%(list_len, rtime, xrtime)
下面的结果表明xrange
确实更快,但还不足以让人流汗。
Loop list of len 1: range=0.0003, xrange=0.0003
Loop list of len 10: range=0.0013, xrange=0.0011
Loop list of len 100: range=0.0068, xrange=0.0034
Loop list of len 1000: range=0.0609, xrange=0.0438
Loop list of len 10000: range=0.5527, xrange=0.5266
Loop list of len 100000: range=10.1666, xrange=7.8481
Loop list of len 1000000: range=168.3425, xrange=155.8719
所以一定要使用xrange
,但除非你在受限的硬件上,否则不要太担心。