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

843

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

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

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__

xrange([start,] stop[, step]) -> xrange object

10

range(x, y)返回一个列表,包含x和y之间的所有数字,如果您使用for循环,则range会更慢。事实上,range具有更大的索引范围。 range(x,y)将打印出x和y之间所有数字的列表。

xrange(x,y)返回xrange(x,y),但是如果您使用for循环,则xrange会更快。xrange具有较小的索引范围。 xrange不仅会打印出xrange(x,y),还将保留其中的所有数字。

[In] range(1,10)
[Out] [1, 2, 3, 4, 5, 6, 7, 8, 9]
[In] xrange(1,10)
[Out] xrange(1,10)

如果您使用for循环,那么它将有效。

[In] for i in range(1,10):
        print i
[Out] 1
      2
      3
      4
      5
      6
      7
      8
      9
[In] for i in xrange(1,10):
         print i
[Out] 1
      2
      3
      4
      5
      6
      7
      8
      9

使用循环时并没有太大的区别,但在仅打印输出时是有区别的!


8
一些其他答案提到Python 3删除了2.x版本的range并将2.x版本的xrange重命名为range。但是,除非您使用的是3.0或3.1(没有人应该使用),否则它实际上是一个略有不同的类型。
正如3.1文档所说:

范围对象几乎没有行为:它们仅支持索引、迭代和len函数。

但是,在3.2+中,range是一个完整的序列——它支持扩展切片,并且具有与list相同语义的collections.abc.Sequence的所有方法。*

至少在CPython和PyPy(目前仅有的两个3.2+实现)中,它还具有indexcount方法以及in运算符的常数时间实现(只要传递整数)。这意味着在3.2+中编写123456 in r是合理的,而在2.7或3.1中则是一个可怕的想法。


*在2.6-2.7和3.0-3.1中,issubclass(xrange, collections.Sequence)返回True的事实是错误,在3.2中已被修复,但未进行回溯。


7

在Python 2.x中:

range(x) 返回一个具有x个元素的列表,该列表将在内存中被创建。

>>> a = range(5)
>>> a
[0, 1, 2, 3, 4]

xrange(x) 返回一个xrange对象,它是一个生成器对象,按需生成数字。它们在for循环期间计算(惰性评估)。

对于循环,这比range()稍微快一些,并且更节省内存。

>>> b = xrange(5)
>>> b
xrange(5)

xrange()不是生成器。xrange(n).__iter __()是生成器。 - th3an0maly

6

在循环中测试range和xrange(我知道应该使用timeit,但这是从记忆中迅速利用简单的列表推导示例进行的)我发现了以下结果:

import time

for x in range(1, 10):

    t = time.time()
    [v*10 for v in range(1, 10000)]
    print "range:  %.4f" % ((time.time()-t)*100)

    t = time.time()
    [v*10 for v in xrange(1, 10000)]
    print "xrange: %.4f" % ((time.time()-t)*100)

这将会得到:

$python range_tests.py
range:  0.4273
xrange: 0.3733
range:  0.3881
xrange: 0.3507
range:  0.3712
xrange: 0.3565
range:  0.4031
xrange: 0.3558
range:  0.3714
xrange: 0.3520
range:  0.3834
xrange: 0.3546
range:  0.3717
xrange: 0.3511
range:  0.3745
xrange: 0.3523
range:  0.3858
xrange: 0.3997 <- garbage collection?

或者,在for循环中使用xrange:

range:  0.4172
xrange: 0.3701
range:  0.3840
xrange: 0.3547
range:  0.3830
xrange: 0.3862 <- garbage collection?
range:  0.4019
xrange: 0.3532
range:  0.3738
xrange: 0.3726
range:  0.3762
xrange: 0.3533
range:  0.3710
xrange: 0.3509
range:  0.3738
xrange: 0.3512
range:  0.3703
xrange: 0.3509

我的代码片段是否测试通过?对于xrange的较慢实例有何评论?或者有更好的示例吗?


2
运行像这样的基准测试一次并不能提供精确的时间结果。总会有一些差异。这可能是垃圾回收,或者其他进程正在占用CPU...等等。这就是为什么基准测试通常会运行10-100-1000次的原因。 - Vajk Hermecz
这只是一个匆忙的代码片段输出 - 我运行了几次,但仅限于大约100个,xrange似乎稍微快一些,尽管在Python 3中比较现在已经过时了。 - Dave Everitt
3
这就是 timeit 的作用。它负责运行多次测试,禁用垃圾回收,使用最佳时钟而不是 time 等。 - abarnert

4

在Python中,xrange()和range()的工作方式对于用户来说是相似的,但是它们之间的区别在于如何分配内存。

当我们使用range()时,会为所有生成的变量分配内存,因此不建议在需要生成大量变量时使用它。

另一方面,xrange()每次仅生成一个特定值,并且只能与for循环一起使用以打印所需的所有值。


3

range生成整个列表并返回它。xrange不会这样做,它只在需要时生成列表中的数字。


2

来自帮助文档。

Python 2.7.12

>>> print range.__doc__
range(stop) -> list of integers
range(start, stop[, step]) -> list of integers

Return a list containing an arithmetic progression of integers.
range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0.
When step is given, it specifies the increment (or decrement).
For example, range(4) returns [0, 1, 2, 3].  The end point is omitted!
These are exactly the valid indices for a list of 4 elements.

>>> print xrange.__doc__
xrange(stop) -> xrange object
xrange(start, stop[, step]) -> xrange object

Like range(), but instead of returning a list, returns an object that
generates the numbers in the range on demand.  For looping, this is 
slightly faster than range() and more memory efficient.

Python 3.5.2

>>> print(range.__doc__)
range(stop) -> range object
range(start, stop[, step]) -> range object

Return an object that produces a sequence of integers from start (inclusive)
to stop (exclusive) by step.  range(i, j) produces i, i+1, i+2, ..., j-1.
start defaults to 0, and stop is omitted!  range(4) produces 0, 1, 2, 3.
These are exactly the valid indices for a list of 4 elements.
When step is given, it specifies the increment (or decrement).

>>> print(xrange.__doc__)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'xrange' is not defined

区别显而易见。在Python 2.x中,range返回一个列表,xrange返回一个可迭代的xrange对象。

在Python 3.x中,range变成了Python 2.x中的xrange,而xrange被删除了。


2

大家都已经很好地解释了。但我想亲自看看它。我使用python3。因此,我打开了资源监视器(在Windows中!),首先执行了以下命令:

a=0
for i in range(1,100000):
    a=a+i

然后检查了'In Use'内存的变化。但是变化很小。 接下来,我运行了以下代码:

for i in list(range(1,100000)):
    a=a+i

它立即占用了大量的内存。我相信这一点。

如果您正在使用Python 2X,则需要在第一个代码中将'range()'替换为'xrange()',并将'list(range())'替换为'range()'。


2

range()在Python 2.x中的用法

这个函数本质上是Python 2.x中可用的旧的range()函数,它返回一个包含指定范围内元素的list对象实例。

然而,当需要初始化一个数字范围的列表时,这种实现方式效率太低了。例如,执行for i in range(1000000)命令会非常昂贵,无论是在内存使用还是时间使用方面,因为它需要将此列表存储到内存中。


range()在Python 3.x中的用法以及Python 2.x中的xrange()

Python 3.x引入了range()的新实现(而这个新的实现在Python 2.x中已经通过xrange()函数可用)。

range()利用一种称为惰性求值的策略。新实现引入了range类,它是一个轻量级对象,代表给定范围内所需的元素,而不需要将它们显式地存储在内存中(这听起来像生成器,但惰性求值的概念是不同的)。


例如,考虑以下示例:

# Python 2.x
>>> a = range(10)
>>> type(a)
<type 'list'>
>>> b = xrange(10)
>>> type(b)
<type 'xrange'>

并且。
# Python 3.x
>>> a = range(10)
>>> type(a)
<class 'range'>

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