Should you always favor xrange() over range()?
为什么或为什么不呢?
型
对于性能,特别是在大范围内迭代时,
- 百万千克1
在python 3中,
百万千克1百万千克1
在某些情况下,
百万千克1百万千克1
在所有需要实际列表的情况下,
百万千克1
型
[编辑]有几篇文章提到了2to3工具将如何升级
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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()包装时,范围保持不变。
型
不,它们都有它们的用途:
迭代时使用
1 | for x in xrange(1, one_zillion): |
号
而不是:
1 | for x in range(1, one_zillion): |
另一方面,如果您想要一个数字列表,请使用
1 2 | multiples_of_seven = range(7,100,7) print"Multiples of seven < 100:", multiples_of_seven |
。
型
只有当你需要一份实际的清单时,你才应该偏爱
另一个区别是xrange()不能支持大于c in t的数字,因此如果您想要使用python内置的大量支持来获得范围,则必须使用range()。
1 2 3 4 5 6 7 8 9 | 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没有这个问题:
1 2 3 4 5 | 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) |
型
除非我特别需要一份清单,否则我总是喜欢
型
range()返回列表,xrange()返回xrange对象。
xrange()速度更快,内存效率更高。但收益并不很大。
列表使用的额外内存当然不仅仅是浪费,列表还有更多的功能(切片、重复、插入等等)。在文档中可以找到确切的差异。没有原则,使用需要的东西。
python 3.0仍在开发中,但iirc range()与2.x的x range()非常相似,可以使用list(range())生成列表。
我想说的是,获得具有切片和索引功能的xrange对象并没有那么困难。我已经编写了一些代码,它们工作得非常好,并且在计算(迭代)时和xrange一样快。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | 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
1 2 | >>> zip(range(5), xrange(100000000)) [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)] |
我不建议在前面的示例中使用range而不是xrange,尽管只需要前五个数字,range计算所有的数字,这可能需要很多时间。使用xrange,这不是问题,因为它只计算所需的数字。
是的,我读了@brian的答案:在python 3中,range()无论如何都是一个生成器,xrange()不存在。
型
请选择范围,原因如下:
1)xrange将在更新的python版本中消失。这为您提供了方便的未来兼容性。
2)范围将采用与xrange相关的效率。
虽然在大多数情况下,
1 2 3 4 5 6 7 8 9 10 | 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; for n in range(%d): a += n'%list_len, number=1000) xrtime = timeit.timeit('a=0; for 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) |
下面的结果表明,
1 2 3 4 5 6 7 | 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 |
所以一定要使用
range() :range(1, 10) 返回1到10个数字的列表,并将整个列表保存在内存中。xrange() :与range() 类似,但不返回列表,而是返回一个按需生成范围内数字的对象。对于循环,这比range() 稍快,内存效率更高。像迭代器一样的xrange() 对象,并根据需要生成数字(延迟计算)。
1 2 3 4 5 6 7 8 | 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 |
型
好吧,这里的每个人对于xrange和range的权衡和优势有不同的看法。它们基本上是正确的,xrange是一个迭代器,range扩展并创建一个实际的列表。对于大多数情况,您不会真正注意到两者之间的区别。(可以将map与range一起使用,但不能与xrange一起使用,但它会占用更多的内存。)
不过,我想你们会想听到的是,首选的选择是xrange。由于python 3中的range是一个迭代器,代码转换工具2to3将正确地将xrange的所有用法转换为range,并对range的用法抛出错误或警告。如果您希望以后能够轻松地转换代码,那么您只需要使用xrange,并且在确定需要列表时使用list(xrange)。我是在今年(2008年)芝加哥的Pycon冲刺赛上学到这一点的。