302

最近我开始使用 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,我找不到。

4

6 回答 6

188

一些性能测量,使用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.xrange确实比 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.xrange的大小与 2.x 的大小完全相同xrange——而且,即使它是 10 倍大,构建不必要的列表仍然比范围迭代可能做的任何问题多出大约 10000000 倍。

那么显式for循环而不是内部的 C 循环deque呢?

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.xrange就是被删除的内容。

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

那么,为什么会慢呢?

好吧,首先,他们添加了许多新功能。另一方面,他们在整个地方(尤其是内部迭代)进行了各种具有轻微副作用的更改。并且已经做了很多工作来显着优化各种重要的案例,即使它有时会略微悲观不太重要的案例。把这一切加起来,我并不感到惊讶的是,range尽可能快地迭代 a 现在有点慢。这是没有人会足够关注的次要案例之一。没有人可能有一个真实的用例,这种性能差异是他们代码中的热点。

于 2013-02-22T00:03:10.850 回答
150

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

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

try:
    xrange
except NameError:
    xrange = range
于 2013-02-22T01:09:29.197 回答
22

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 3range稍微快一点:

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]is range(20, 60, 10)!

于 2013-02-21T23:42:07.870 回答
2

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

import sys

if sys.version_info >= (3, 0):
    def xrange(*args, **kwargs):
        return iter(range(*args, **kwargs))
于 2016-01-22T15:18:03.857 回答
1

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

于 2018-04-07T01:52:23.583 回答
-1

comp:~$ python Python 2.7.6 (default, Jun 22 2015, 17:58:13) [GCC 4.8.2] on linux2

>>> 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 参数:

>>> 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)[GCC 4.8.4] 在 linux 上

>>> 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 参数可以快速且线性地工作:

>>> 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()。

于 2015-12-27T10:46:21.857 回答