Python:为什么我基于生成器的范围是X2慢于xrange?

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)

出于好奇,我已经用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)
    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
    可以利用类型限制(只使用“短”整数)

    这是一个有趣的测试。看看on
    xrange
    ,我想到的一个猜测是
    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)