为什么Python3中没有xrange函数?

时间:2013-02-21 23:36:54

标签: python python-3.x pep 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 个答案:

答案 0 :(得分:161)

使用timeit进行一些性能测量,而不是尝试使用time手动执行。

首先,Apple 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已经针对32位的方式针对64位性能进行了优化。

但这真的很重要吗?再次检查这一点,再次使用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

因此,构建list所需的时间是整个迭代的两倍多。

至于“消耗比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被移除了,无论人们多少次告诉你同样的事情,但我会再次重复:它没有删除:它被重命名为{{1} },而2.x range就是删除的内容。

这里有一些证据证明3.3 range对象是2.x range对象的直接后代(而不是2.x xrange函数):来源{ {3}}和3.3 range。您甚至可以看到2.7 xrange(我相信,链接到文件中任何位置替换字符串“xrange”的最后一个实例的更改)。

那么,为什么它会变慢?

嗯,首先,他们添加了许多新功能。另一方面,他们在整个地方(特别是在迭代内部)做了各种各样的改变,这些改变都有轻微的副作用。并且已经做了很多工作来大幅优化各种重要案例,即使它有时会使不太重要的案例略微悲观。加上这一切,我并不感到惊讶的是,尽可能快地迭代range现在有点慢。这是一个不那么重要的案例,没有人会关注到足够关注。任何人都不可能有一个真实的用例,其中这种性能差异是他们代码中的热点。

答案 1 :(得分:132)

Python3的范围 Python2的xrange。没有必要围绕它包裹它。要在Python3中获取实际列表,您需要使用list(range(...))

如果你想要一些适用于Python2和Python3的东西,试试这个

try:
    xrange
except NameError:
    xrange = range

答案 2 :(得分:15)

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(15, 60, 10)

答案 3 :(得分:0)

修复python2代码的一种方法是:

import sys

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

答案 4 :(得分:0)

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

答案 5 :(得分:-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

时间数= 1参数:

>>> 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

时间数= 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的工作原理足够快,但在重复循环中,python 2 xrange()在python 3中对range()的速度获胜。