问题:您是否应该始终偏爱xrange()而不是range()?

为什么或者为什么不?


回答 0

对于性能,尤其是在较大范围内进行迭代时,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()包装的地方,范围保持不变。

For performance, especially when you’re iterating over a large range, xrange() is usually better. However, there are still a few cases why you might prefer range():

  • In python 3, range() does what xrange() used to do and xrange() does not exist. If you want to write code that will run on both Python 2 and Python 3, you can’t use xrange().

  • range() can actually be faster in some cases – eg. if iterating over the same sequence multiple times. xrange() has to reconstruct the integer object every time, but range() will have real integer objects. (It will always perform worse in terms of memory however)

  • xrange() isn’t usable in all cases where a real list is needed. For instance, it doesn’t support slices, or any list methods.

[Edit] There are a couple of posts mentioning how range() will be upgraded by the 2to3 tool. For the record, here’s the output of running the tool on some sample usages of range() and 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)

As you can see, when used in a for loop or comprehension, or where already wrapped with list(), range is left unchanged.


回答 1

不,它们都有各自的用途:

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

No, they both have their uses:

Use xrange() when iterating, as it saves memory. Say:

for x in xrange(1, one_zillion):

rather than:

for x in range(1, one_zillion):

On the other hand, use range() if you actually want a list of numbers.

multiples_of_seven = range(7,100,7)
print "Multiples of seven < 100: ", multiples_of_seven

回答 2

仅当需要实际列表时range()xrange()才应优先考虑。例如,当您想修改所返回的列表range()时,或者希望对其进行切片时。对于迭代,甚至只是普通索引,xrange()都可以正常工作(通常效率更高)。有一点range()要比xrange()很小的列表快一点,但是取决于您的硬件和其他各种细节,收支平衡可能是长度为1或2的结果;不用担心。更喜欢xrange()

You should favour range() over xrange() only when you need an actual list. For instance, when you want to modify the list returned by range(), or when you wish to slice it. For iteration or even just normal indexing, xrange() will work fine (and usually much more efficiently). There is a point where range() is a bit faster than xrange() for very small lists, but depending on your hardware and various other details, the break-even can be at a result of length 1 or 2; not something to worry about. Prefer xrange().


回答 3

另一个区别是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)

One other difference is that xrange() can’t support numbers bigger than C ints, so if you want to have a range using python’s built in large number support, you have to use 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 does not have this problem:

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)

回答 4

xrange()效率更高,因为它无需生成对象列表,而只是一次生成一个对象。一次只能有一个整数,而不是100个整数及其所有开销和放入它们的列表。生成速度更快,内存使用更好,代码更高效。

除非我特别需要某件事的清单,否则我总是会支持 xrange()

xrange() is more efficient because instead of generating a list of objects, it just generates one object at a time. Instead of 100 integers, and all of their overhead, and the list to put them in, you just have one integer at a time. Faster generation, better memory use, more efficient code.

Unless I specifically need a list for something, I always favor xrange()


回答 5

range()返回一个列表,xrange()返回一个xrange对象。

xrange()更快,内存使用效率更高。但是收益不是很大。

列表使用的额外内存当然不只是浪费,列表还具有更多功能(切片,重复,插入等)。确切的差异可以在文档中找到。没有硬性规定,请使用所需的。

Python 3.0仍在开发中,但是IIRC range()与2.X的xrange()非常相似,并且list(range())可用于生成列表。

range() returns a list, xrange() returns an xrange object.

xrange() is a bit faster, and a bit more memory efficient. But the gain is not very large.

The extra memory used by a list is of course not just wasted, lists have more functionality (slice, repeat, insert, …). Exact differences can be found in the documentation. There is no bonehard rule, use what is needed.

Python 3.0 is still in development, but IIRC range() will very similar to xrange() of 2.X and list(range()) can be used to generate lists.


回答 6

我只想说,获取具有切片和索引功能的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都应该做所有这一切……

I would just like to say that it REALLY isn’t that difficult to get an xrange object with slice and indexing functionality. I have written some code that works pretty dang well and is just as fast as xrange for when it counts (iterations).

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)

Honestly, I think the whole issue is kind of silly and xrange should do all of this anyway…


回答 7

书中的一个很好的例子:Magnus Lie Hetland的《实用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()不存在。

A good example given in book: Practical Python By Magnus Lie Hetland

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

I wouldn’t recommend using range instead of xrange in the preceding example—although only the first five numbers are needed, range calculates all the numbers, and that may take a lot of time. With xrange, this isn’t a problem because it calculates only those numbers needed.

Yes I read @Brian’s answer: In python 3, range() is a generator anyway and xrange() does not exist.


回答 8

出于以下原因选择范围:

1)xrange将在较新的Python版本中消失。这样可以轻松实现将来的兼容性。

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

Go with range for these reasons:

1) xrange will be going away in newer Python versions. This gives you easy future compatibility.

2) range will take on the efficiencies associated with xrange.


回答 9

好的,对于xrange与range的权衡和优势,每个人都有不同的看法。它们大体上是正确的,xrange是一个迭代器,range充实并创建实际列表。在大多数情况下,您不会真正注意到两者之间的差异。(您可以将map与range一起使用,但不能与xrange一起使用,但是会占用更多内存。)

但是,我认为您希望聚会中听到的是,首选是xrange。由于Python 3中的range是一个迭代器,因此代码转换工具2to3可以将xrange的所有用法正确转换为range,并且会针对range的使用抛出错误或警告。如果要确保将来轻松转换代码,则仅使用xrange,并在确定需要列表时使用list(xrange)。我是在今年(2008年)在芝加哥PyCon上进行的CPython冲刺中学到的。

Okay, everyone here as a different opinion as to the tradeoffs and advantages of xrange versus range. They’re mostly correct, xrange is an iterator, and range fleshes out and creates an actual list. For the majority of cases, you won’t really notice a difference between the two. (You can use map with range but not with xrange, but it uses up more memory.)

What I think you rally want to hear, however, is that the preferred choice is xrange. Since range in Python 3 is an iterator, the code conversion tool 2to3 will correctly convert all uses of xrange to range, and will throw out an error or warning for uses of range. If you want to be sure to easily convert your code in the future, you’ll use xrange only, and list(xrange) when you’re sure that you want a list. I learned this during the CPython sprint at PyCon this year (2008) in Chicago.


回答 10

  • 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()xrange()执行与Python 3中相同的操作,并且在Python 3中不xrange()存在术语。 range()如果多次迭代同一序列,在某些情况下实际上可以更快。xrange()每次都必须重新构造整数对象,但是range()将拥有真正的整数对象。

  • range(): range(1, 10) returns a list from 1 to 10 numbers & hold whole list in memory.
  • xrange(): Like range(), but instead of returning a list, returns an object that generates the numbers in the range on demand. For looping, this is lightly faster than range() and more memory efficient. xrange() object like an iterator and generates the numbers on demand (Lazy Evaluation).
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() does the same thing as xrange() used to do in Python 3 and there is not term xrange() exist in Python 3. range() can actually be faster in some scenario if you iterating over the same sequence multiple times. xrange() has to reconstruct the integer object every time, but range() will have real integer objects.


回答 11

虽然xrangerange大多数情况下要快,但性能差异却很小。下面的小程序比较了对a range和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,但是除非您使用的是受限制的硬件,否则不要为它担心太多。

While xrange is faster than range in most circumstances, the difference in performance is pretty minimal. The little program below compares iterating over a range and 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)

The results below shows that xrange is indeed faster, but not enough to sweat over.

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

So by all means use xrange, but unless you’re on a constrained hardware, don’t worry too much about it.


声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。