问题:为什么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,我找不到它。
干杯。
回答 0
进行一些性能评估,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__
在循环中发生的任何事情的1000 万次调用中,对插槽的一次性调用不太可能可见,但有人提出来了。)
但这仅慢了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
因此,构建所需的list
时间是整个迭代的两倍以上。
至于“比Python 2.6+消耗更多的资源”,在我的测试中,看起来3.x range
的大小与2.x的大小完全相同xrange
,即使它的大小是10x的大小,也可以构建不必要的列表问题仍然比范围迭代可能做的任何事情多出10000000x。
那么显式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.x range
是被删除的东西。
这是3.3 range
对象是2.x xrange
对象(而不是2.x range
函数)的直接后代的证明:3.3range
和2.7xrange
的源。您甚至可以查看更改历史记录(我相信,该更改已链接到替换文件中任何位置的字符串“ xrange”的最后一个实例的更改)。
那么,为什么它变慢?
好吧,其中之一是,他们添加了许多新功能。另外,他们已经在整个地方(尤其是在迭代过程中)进行了各种具有较小副作用的更改。尽管有时有时会稍微低估不太重要的案例,但仍有大量工作可以极大地优化各种重要案例。将所有这些加起来,令迭代range
速度现在变慢会令我感到惊讶。这是最重要的案例之一,没有人会足够关注。没有人会遇到现实中的用例,这种性能差异是他们代码中的热点。
回答 1
Python3的范围是 Python2的xrange。无需在其周围包装迭代器。要获得Python3中的实际列表,您需要使用list(range(...))
如果您想要适用于Python2和Python3的产品,请尝试以下操作
try:
xrange
except NameError:
xrange = range
回答 2
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]
is range(20, 60, 10)
!
回答 3
修复python2代码的一种方法是:
import sys
if sys.version_info >= (3, 0):
def xrange(*args, **kwargs):
return iter(range(*args, **kwargs))
回答 4
Python 2中的xrange是一个生成器,它实现了迭代器,而range只是一个函数。在Python3中,我不知道为什么将其从xrange中删除。
回答 5
comp:〜$ python Python 2.7.6(默认,2015年6月22日,17:58:13)[gcc 4.8.2]在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表示x在range(1000000)中,如果x%4]”,number = 1)(正如我们在实际代码中实际使用的那样),python3的运行速度足够快,但在重复循环中,python 2 xrange()在速度上胜过python 3中的range()。