Python:为什么我基于生成器的范围是X2慢于xrange?
出于好奇,我已经用Python编写了3个测试,并使用timeit将它们超时:Python:为什么我基于生成器的范围是X2慢于xrange?,python,performance,cpython,Python,Performance,Cpython,出于好奇,我已经用Python编写了3个测试,并使用timeit将它们超时: import timeit # simple range based on generator def my_range(start, stop): i = start while (i < stop): yield i i += 1 # test regular range def test_range(): x = range(1, 100000)
import timeit
# simple range based on generator
def my_range(start, stop):
i = start
while (i < stop):
yield i
i += 1
# test regular range
def test_range():
x = range(1, 100000)
sum = 0
for i in x:
sum += i
# test xrange
def test_xrange():
x = xrange(1, 100000)
sum = 0
for i in x:
sum += i
# test my range
def test_my_range():
x = my_range(1, 100000)
sum = 0
for i in x:
sum += i
print timeit.timeit("test_range()", setup = "from __main__ import test_range", number = 100)
print timeit.timeit("test_xrange()", setup = "from __main__ import test_xrange", number = 100)
print timeit.timeit("test_my_range()", setup = "from __main__ import test_my_range", number = 100)
我的射程比创建列表的射程还要慢。为什么?
xrange()
/range()
是否直接使用C实现谢谢 这是一个有趣的测试。查看on
xrange
,我想到的一个猜测是xrange
可以利用类型限制(只使用“短”整数)这是一个有趣的测试。看看onxrange
,我想到的一个猜测是xrange
可以利用类型限制(只使用“短”整数)我觉得简单的答案是xrange()
是内置的并用C编写的
我在您的测试中添加了另一个案例(见下文):基于CPython源代码的xrange()
import timeit
from collections import Sequence, Iterator
from math import ceil
# simple range based on generator
def my_range(start, stop):
i = start
while (i < stop):
yield i
i += 1
# test regular range
def test_range():
x = range(1, 100000)
sum = 0
for i in x:
sum += i
# test xrange
def test_xrange():
x = xrange(1, 100000)
sum = 0
for i in x:
sum += i
# test my range
def test_my_range():
x = my_range(1, 100000)
sum = 0
for i in x:
sum += i
class pure_python_xrange(Sequence):
"""Pure-Python implementation of an ``xrange`` (aka ``range``
in Python 3) object. See `the CPython documentation
<http://docs.python.org/py3k/library/functions.html#range>`_
for details.
"""
def __init__(self, *args):
if len(args) == 1:
start, stop, step = 0, args[0], 1
elif len(args) == 2:
start, stop, step = args[0], args[1], 1
elif len(args) == 3:
start, stop, step = args
else:
raise TypeError('pure_python_xrange() requires 1-3 int arguments')
try:
start, stop, step = int(start), int(stop), int(step)
except ValueError:
raise TypeError('an integer is required')
if step == 0:
raise ValueError('pure_python_xrange() arg 3 must not be zero')
elif step < 0:
stop = min(stop, start)
else:
stop = max(stop, start)
self._start = start
self._stop = stop
self._step = step
self._len = (stop - start) // step + bool((stop - start) % step)
def __repr__(self):
if self._start == 0 and self._step == 1:
return 'pure_python_xrange(%d)' % self._stop
elif self._step == 1:
return 'pure_python_xrange(%d, %d)' % (self._start, self._stop)
return 'pure_python_xrange(%d, %d, %d)' % (self._start, self._stop, self._step)
def __eq__(self, other):
return isinstance(other, xrange) and \
self._start == other._start and \
self._stop == other._stop and \
self._step == other._step
def __len__(self):
return self._len
def index(self, value):
"""Return the 0-based position of integer `value` in
the sequence this xrange represents."""
diff = value - self._start
quotient, remainder = divmod(diff, self._step)
if remainder == 0 and 0 <= quotient < self._len:
return abs(quotient)
raise ValueError('%r is not in range' % value)
def count(self, value):
"""Return the number of ocurrences of integer `value`
in the sequence this xrange represents."""
# a value can occur exactly zero or one times
return int(value in self)
def __contains__(self, value):
"""Return ``True`` if the integer `value` occurs in
the sequence this xrange represents."""
try:
self.index(value)
return True
except ValueError:
return False
def __reversed__(self):
"""Return an xrange which represents a sequence whose
contents are the same as the sequence this xrange
represents, but in the opposite order."""
sign = self._step / abs(self._step)
last = self._start + ((self._len - 1) * self._step)
return pure_python_xrange(last, self._start - sign, -1 * self._step)
def __getitem__(self, index):
"""Return the element at position ``index`` in the sequence
this xrange represents, or raise :class:`IndexError` if the
position is out of range."""
if isinstance(index, slice):
return self.__getitem_slice(index)
if index < 0:
# negative indexes access from the end
index = self._len + index
if index < 0 or index >= self._len:
raise IndexError('xrange object index out of range')
return self._start + index * self._step
def __getitem_slice(self, slce):
"""Return an xrange which represents the requested slce
of the sequence represented by this xrange.
"""
start, stop, step = slce.start, slce.stop, slce.step
if step == 0:
raise ValueError('slice step cannot be 0')
start = start or self._start
stop = stop or self._stop
if start < 0:
start = max(0, start + self._len)
if stop < 0:
stop = max(start, stop + self._len)
if step is None or step > 0:
return pure_python_xrange(start, stop, step or 1)
else:
rv = reversed(self)
rv._step = step
return rv
def __iter__(self):
"""Return an iterator which enumerates the elements of the
sequence this xrange represents."""
return xrangeiterator(self)
class xrangeiterator(Iterator):
"""An iterator for an :class:`xrange`.
"""
def __init__(self, xrangeobj):
self._xrange = xrangeobj
# Intialize the "last outputted value" to the value
# just before the first value; this simplifies next()
self._last = self._xrange._start - self._xrange._step
self._count = 0
def __iter__(self):
"""An iterator is already an iterator, so return ``self``.
"""
return self
def next(self):
"""Return the next element in the sequence represented
by the xrange we are iterating, or raise StopIteration
if we have passed the end of the sequence."""
self._last += self._xrange._step
self._count += 1
if self._count > self._xrange._len:
raise StopIteration()
return self._last
# test xrange
def test_pure_python_xrange():
x = pure_python_xrange(1, 100000)
sum = 0
for i in x:
sum += i
print timeit.timeit("test_range()", setup = "from __main__ import test_range", number = 100)
print timeit.timeit("test_xrange()", setup = "from __main__ import test_xrange", number = 100)
print timeit.timeit("test_my_range()", setup = "from __main__ import test_my_range", number = 100)
print timeit.timeit("test_pure_python_xrange()", setup = "from __main__ import test_pure_python_xrange", number = 100)
import timeit
从集合导入序列,迭代器
从数学导入单元
#基于生成器的简单范围
def my_范围(启动、停止):
i=开始
而(我<停止):
产量一
i+=1
#测试正常范围
def测试_范围():
x=范围(100000)
总和=0
对于x中的i:
总和+=i
#测试范围
def测试范围():
x=x范围(100000)
总和=0
对于x中的i:
总和+=i
#测试我的射程
def test_my_range():
x=我的_范围(100000)
总和=0
对于x中的i:
总和+=i
类纯python类范围(序列):
“”“一个``xrange`(又称``range`)的纯Python实现``
在Python3)对象中。请参见` CPython文档
`_
详情请参阅。
"""
定义初始化(self,*args):
如果len(args)==1:
开始,停止,步骤=0,参数[0],1
elif len(args)=2:
开始、停止、步骤=参数[0],参数[1],1
elif len(args)=3:
开始、停止、步骤=args
其他:
raise TypeError('pure_python_xrange()需要1-3个整型参数')
尝试:
开始,停止,步骤=int(开始),int(停止),int(步骤)
除值错误外:
raise TypeError('需要整数')
如果步骤==0:
raise VALUERROR('pure_python_xrange()arg 3不能为零')
elif步骤<0:
停止=最小值(停止,启动)
其他:
停止=最大值(停止,启动)
self.\u start=start
自停
自我。_步骤=步骤
self.\u len=(停止-启动)//步骤+bool((停止-启动)%step)
定义报告(自我):
如果self.\u start==0,self.\u step==1:
返回“纯python范围(%d)”%self.\u停止
elif self.\u步骤==1:
返回“纯python范围(%d,%d)”(自启动、自停止)
返回“纯python范围(%d,%d,%d)”(自开始、自停止、自步骤)
定义(自身、其他):
返回isinstance(其他,X范围)和\
self.\u start==其他。\u start和\
self.\u stop==其他.\u stop and\
自身步骤==其他步骤
定义(自我):
返回自我
def索引(自身,值):
“”“返回中整型'value'的从0开始的位置”
此范围表示的序列。”“”
diff=值-自身。\u开始
商,余数=divmod(差分,自步)
如果余数=0且0=self.\u len:
提升索引器('xrange对象索引超出范围')
返回self.\u开始+索引*self.\u步骤
定义获取项目切片(自、slce):
“”“返回表示请求的slce的xrange
此范围表示的序列的。
"""
开始,停止,步骤=slce.start,slce.stop,slce.step
如果步骤==0:
raise VALUERROR('切片步骤不能为0')
开始=开始或自我。\u开始
停止=停止或自行
如果开始<0:
开始=最大值(0,开始+自身)
如果停止<0:
停止=最大值(启动、停止+自身)
如果步骤为无或步骤>0:
返回纯python范围(开始、停止、步骤或1)
其他:
rv=反向(自)
rv.\u步骤=步骤
返回rv
定义(自我):
“”“返回一个迭代器,该迭代器枚举
此范围表示的序列。”“”
返回xrangeiterator(self)
类xrangeiterator(迭代器):
“”“类:`xrange`的迭代器。”。
"""
定义初始化(self,xrangeobj):
self.\u xrange=xrangeobj
#将“上次输出的值”初始化为
#就在第一个值之前;这简化了下一步()
self.\u last=self.\u xrange.\u start-self.\u xrange.\u步长
自计数=0
定义(自我):
“”“迭代器已经是迭代器,因此返回`self``。
"""
回归自我
def next(自我):
“”“返回所表示序列中的下一个元素。”
根据我们正在迭代的xrange,或者提出StopIteration
如果我们已经通过了序列的末尾。”“”
self.\u last+=self.\u xrange.\u步长
自身计数+=1
如果self.\u count>self.\u xrange.\u len:
提升停止迭代()
返回自我
#测试范围
def test_pure_python_xrange():
x=纯python范围(100000)
总和=0
对于x中的i:
总和+=i
打印timeit.timeit(“test\u range()”,setup=“from\uuuuu main\uuuuu导入test\u range”,number=100)
打印timeit.timeit(“test\u xrange()”,setup=“from\uuuuu main\uuuuuu导入test\u xrange”,编号=100)
打印timeit.timeit(“test\u my\u range()”,setup=“from\uu main\uuuu导入test\u my\u range”,number=100)
打印timeit.timeit(“test\u pure\u python\u xrange()”,setup=“from\uuu main\uuuuu导入test\u pure\u python\u xrange”,数字=100)
结果如何
$python so.py
0.426695823669
0.371111869812
0.964643001556
6.06390094757
我是si
import timeit
from collections import Sequence, Iterator
from math import ceil
# simple range based on generator
def my_range(start, stop):
i = start
while (i < stop):
yield i
i += 1
# test regular range
def test_range():
x = range(1, 100000)
sum = 0
for i in x:
sum += i
# test xrange
def test_xrange():
x = xrange(1, 100000)
sum = 0
for i in x:
sum += i
# test my range
def test_my_range():
x = my_range(1, 100000)
sum = 0
for i in x:
sum += i
class pure_python_xrange(Sequence):
"""Pure-Python implementation of an ``xrange`` (aka ``range``
in Python 3) object. See `the CPython documentation
<http://docs.python.org/py3k/library/functions.html#range>`_
for details.
"""
def __init__(self, *args):
if len(args) == 1:
start, stop, step = 0, args[0], 1
elif len(args) == 2:
start, stop, step = args[0], args[1], 1
elif len(args) == 3:
start, stop, step = args
else:
raise TypeError('pure_python_xrange() requires 1-3 int arguments')
try:
start, stop, step = int(start), int(stop), int(step)
except ValueError:
raise TypeError('an integer is required')
if step == 0:
raise ValueError('pure_python_xrange() arg 3 must not be zero')
elif step < 0:
stop = min(stop, start)
else:
stop = max(stop, start)
self._start = start
self._stop = stop
self._step = step
self._len = (stop - start) // step + bool((stop - start) % step)
def __repr__(self):
if self._start == 0 and self._step == 1:
return 'pure_python_xrange(%d)' % self._stop
elif self._step == 1:
return 'pure_python_xrange(%d, %d)' % (self._start, self._stop)
return 'pure_python_xrange(%d, %d, %d)' % (self._start, self._stop, self._step)
def __eq__(self, other):
return isinstance(other, xrange) and \
self._start == other._start and \
self._stop == other._stop and \
self._step == other._step
def __len__(self):
return self._len
def index(self, value):
"""Return the 0-based position of integer `value` in
the sequence this xrange represents."""
diff = value - self._start
quotient, remainder = divmod(diff, self._step)
if remainder == 0 and 0 <= quotient < self._len:
return abs(quotient)
raise ValueError('%r is not in range' % value)
def count(self, value):
"""Return the number of ocurrences of integer `value`
in the sequence this xrange represents."""
# a value can occur exactly zero or one times
return int(value in self)
def __contains__(self, value):
"""Return ``True`` if the integer `value` occurs in
the sequence this xrange represents."""
try:
self.index(value)
return True
except ValueError:
return False
def __reversed__(self):
"""Return an xrange which represents a sequence whose
contents are the same as the sequence this xrange
represents, but in the opposite order."""
sign = self._step / abs(self._step)
last = self._start + ((self._len - 1) * self._step)
return pure_python_xrange(last, self._start - sign, -1 * self._step)
def __getitem__(self, index):
"""Return the element at position ``index`` in the sequence
this xrange represents, or raise :class:`IndexError` if the
position is out of range."""
if isinstance(index, slice):
return self.__getitem_slice(index)
if index < 0:
# negative indexes access from the end
index = self._len + index
if index < 0 or index >= self._len:
raise IndexError('xrange object index out of range')
return self._start + index * self._step
def __getitem_slice(self, slce):
"""Return an xrange which represents the requested slce
of the sequence represented by this xrange.
"""
start, stop, step = slce.start, slce.stop, slce.step
if step == 0:
raise ValueError('slice step cannot be 0')
start = start or self._start
stop = stop or self._stop
if start < 0:
start = max(0, start + self._len)
if stop < 0:
stop = max(start, stop + self._len)
if step is None or step > 0:
return pure_python_xrange(start, stop, step or 1)
else:
rv = reversed(self)
rv._step = step
return rv
def __iter__(self):
"""Return an iterator which enumerates the elements of the
sequence this xrange represents."""
return xrangeiterator(self)
class xrangeiterator(Iterator):
"""An iterator for an :class:`xrange`.
"""
def __init__(self, xrangeobj):
self._xrange = xrangeobj
# Intialize the "last outputted value" to the value
# just before the first value; this simplifies next()
self._last = self._xrange._start - self._xrange._step
self._count = 0
def __iter__(self):
"""An iterator is already an iterator, so return ``self``.
"""
return self
def next(self):
"""Return the next element in the sequence represented
by the xrange we are iterating, or raise StopIteration
if we have passed the end of the sequence."""
self._last += self._xrange._step
self._count += 1
if self._count > self._xrange._len:
raise StopIteration()
return self._last
# test xrange
def test_pure_python_xrange():
x = pure_python_xrange(1, 100000)
sum = 0
for i in x:
sum += i
print timeit.timeit("test_range()", setup = "from __main__ import test_range", number = 100)
print timeit.timeit("test_xrange()", setup = "from __main__ import test_xrange", number = 100)
print timeit.timeit("test_my_range()", setup = "from __main__ import test_my_range", number = 100)
print timeit.timeit("test_pure_python_xrange()", setup = "from __main__ import test_pure_python_xrange", number = 100)