为什么 Python3 中没有 xrange 函数?

最近我开始使用Python3,它缺乏xrange,这让我很受伤。

简单的例子。

1) Python2:

from time import time as t
def count():
  st = t()
  [x for x in xrange(10000000) if x%4 == 0]
  et = t()
  print et-st
count()

2) Python3:

from time import time as t

def xrange(x):

    return iter(range(x))

def count():
    st = t()
    [x for x in xrange(10000000) if x%4 == 0]
    et = t()
    print (et-st)
count()

结果分别为:。

1) 1.53888392448 2) 3.215819835662842

为什么会这样?我的意思是,为什么xrange'被删除?它是一个很好的学习工具。对于初学者来说,就像我一样,就像我们在某些时候都是这样。为什么要删除它?谁能给我指出正确的PEP,我找不到它。

干杯。

对该问题的评论 (6)
解决办法

一些性能测量,使用timeit,而不是试图用time手动完成。

首先,苹果2.7.2 64位。

In [37]: %timeit collections.deque((x for x in xrange(10000000) if x%4 == 0), maxlen=0)
1 loops, best of 3: 1.05 s per loop

现在,python.org 3.3.0 64位。

In [83]: %timeit collections.deque((x for x in range(10000000) if x%4 == 0), maxlen=0)
1 loops, best of 3: 1.32 s per loop

In [84]: %timeit collections.deque((x for x in xrange(10000000) if x%4 == 0), maxlen=0)
1 loops, best of 3: 1.31 s per loop

In [85]: %timeit collections.deque((x for x in iter(range(10000000)) if x%4 == 0), maxlen=0) 
1 loops, best of 3: 1.33 s per loop

显然,3.x的range确实比2.x的xrange慢一点。而OP'的xrange函数与此无关。(这并不奇怪,因为对__iter__槽的一次性调用不可能在对循环中发生的任何事情的10000000次调用中被看到,但有人把它作为一种可能性提出来)。

但是它只慢了30%。OP是如何做到2倍慢的呢?好吧,如果我用32位Python重复同样的测试,我得到1.58比3.12。所以我的猜测是,这又是一个3.x被优化为64位性能而损害32位的例子。

但这真的有关系吗?再看看这个,用3.3.0 64位的。

In [86]: %timeit [x for x in range(10000000) if x%4 == 0]
1 loops, best of 3: 3.65 s per loop

所以,建立列表所需时间是整个迭代过程的两倍多。

至于 "比 Python 2.6+ 消耗更多的资源",从我的测试来看,3.x 的 range 和 2.x 的 xrange 大小完全一样,而且,即使它是 10 倍的大小,建立不必要的列表仍然是比范围迭代可能做的事情多 10000000 倍的问题。

那么用一个明确的for'循环代替deque'里面的C循环呢?

In [87]: def consume(x):
   ....:     for i in x:
   ....:         pass
In [88]: %timeit consume(x for x in range(10000000) if x%4 == 0)
1 loops, best of 3: 1.85 s per loop

因此,在 "for "语句中浪费的时间几乎与在 "range "迭代的实际工作中浪费的时间一样多。

如果你担心优化一个范围对象的迭代,你可能找错地方了。


同时,你一直在问为什么xrange被删除了,不管人们告诉你多少次同样的事情,但我要再次重复。它没有被删除:它被重新命名为range,2.x的range就是被删除的。

这里有一些证据表明3.3 range对象是2.x range对象的直接后代(而不是2.x range函数的后代):3.3 range2.7 xrange的来源。你甚至可以看到更改历史(链接到,我相信,替换了文件中任何地方的字符串"xrange" 的最后一个实例的更改)。

那么,为什么它比较慢呢?

嗯,首先,他们增加了很多新的功能。另一个原因是,他们在所有地方(尤其是在迭代内部)做了各种变化,这些变化都有轻微的副作用。还有,他们做了很多工作来大幅优化各种重要的情况,即使有时会对不太重要的情况稍作估计。把这一切加起来,我并不惊讶,尽可能快地迭代一个 "范围",现在却有点慢了。这是那些不太重要的情况之一,没有人会关心到要关注它。没有人可能在现实生活中遇到这种性能差异是他们代码中的热点的用例。

评论(5)

Python3的范围_就是Python2的xrange。没有必要在它周围包裹一个 iter。要在Python3中得到一个真正的列表,你需要使用 list(range(...))

如果你想在Python2和Python3中都能使用,可以试试这个方法

try:
    xrange
except NameError:
    xrange = range
评论(3)

Python 3的range类型与Python 2的xrange一样工作。我不确定你为什么会看到速度变慢,因为你的xrange函数返回的迭代器正是你在range上直接迭代得到的。

我无法在我的系统上重现这种慢速。以下是我的测试方法。

Python 2,使用xrange

Python 2.7.3 (default, Apr 10 2012, 23:24:47) [MSC v.1500 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import timeit
>>> timeit.timeit("[x for x in xrange(1000000) if x%4]",number=100)
18.631936646865853

Python 3,使用range会快一点点。

Python 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:57:17) [MSC v.1600 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import timeit
>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=100)
17.31399508687869

我最近了解到,Python 3'的range类型还有一些整洁的特性,比如支持切片。range(10,100,2)[5:25:5]range(20, 60, 10)!

评论(15)

修正你的python2代码的一个方法是。

import sys

if sys.version_info >= (3, 0):
    def xrange(*args, **kwargs):
        return iter(range(*args, **kwargs))
评论(7)

Python 2 中的 xrange 是一个生成器,实现了迭代器,而 range 只是一个函数。 在Python3中,我不知道为什么xrange被删除了。

评论(2)

comp:~$ python Python 2.7.6 (默认,2015年6月22日,17:58:13) 在linux2上的[GCC 4.8.2]。

>>> import timeit
>>> timeit.timeit("[x for x in xrange(1000000) if x%4]",number=100)

5.656799077987671

>>> timeit.timeit("[x for x in xrange(1000000) if x%4]",number=100)

5.579368829727173

>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=100)

21.54827117919922

>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=100)

22.014557123184204

用timeit number=1 param:

>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=1)

0.2245171070098877

>>> timeit.timeit("[x for x in xrange(1000000) if x%4]",number=1)

0.10750913619995117

comp:~$ python3。 Python 3.4.3 (默认,2015年10月14日,20:28:29) linux上的[GCC 4.8.4]。

>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=100)

9.113872020003328

>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=100)

9.07014398300089

用timeit number=1,2,3,4 param快速线性工作:

>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=1)

0.09329321900440846

>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=2)

0.18501482300052885

>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=3)

0.2703447980020428

>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=4)

0.36209142999723554

所以,如果我们测量1个运行的循环周期,比如timeit.timeit("[x for x in range(1000000) if x%4]",number=1)(因为我们在实际代码中实际使用),python3的工作速度足够快,但在重复循环中,python2的xrange()在速度上胜过python3的range()。

评论(1)