如果你总是喜欢xrange()超过范围()?

为什么或者为什么不?

为了提高性能,特别是在迭代大范围时, xrange()通常更好。 但是,仍然有一些情况为什么你可能更喜欢range()

  • 在python 3中, range()xrange()用来做和xrange()不存在。 如果您想编写将在Python 2和Python 3上运行的代码,则不能使用xrange()

  • range()在某些情况下实际上可能更快 – 例如。 如果多次迭代相同的序列。 xrange()必须每次重建整数对象,但range()将有实数整数对象。 (然而,内存方面总是会变差)

  • xrange()在所有需要真实列表的情况下都不可用。 例如,它不支持切片或任何列表方法。

[编辑]有几个帖子提到如何通过2to3工具升级range() 。 为了记录,这里是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()

另外一个区别是xrange()不能支持比C ints更大的数字,所以如果你想使用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也应该这样做。

书中给出了一个很好的例子: 实用Python作者:Magnus Lie Hetland

 >>> zip(range(5), xrange(100000000)) [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)] 

在前面的例子中,我不推荐使用范围而不是xrange,尽管只需要前五个数字,范围计算所有数字,这可能需要很长时间。 与xrange,这不是一个问题,因为它只计算所需的数字。

是的,我读@ Brian的答案:在python 3中,range()是一个生成器,而xrange()不存在。

出于以下原因,请使用范围:

1)xrange将会在更新的Python版本中消失。 这给你容易的未来兼容性。

2)范围将承担与xrange相关的效率。

好的,这里的每个人都认为xrange和range的权衡和优势是不同的。 他们大部分是正确的,xrange是一个迭代器,范围充实,并创建一个实际的列表。 大多数情况下,你不会真正注意到两者之间的差异。 (你可以使用map而不是xrange,但是会占用更多的内存。)

然而,我认为你们想要听到的是,首选是xrange。 由于Python 3中的范围是一个迭代器,因此代码转换工具2to3将正确地将xrange的所有用法转换为范围,并且会针对范围的使用抛出错误或警告。 如果您希望将来可以轻松转换代码,那么您只能使用xrange,并且在确定需要列表时列出(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和一个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 ,但除非你在一个受限制的硬件上,不要太担心。