为什么使用切片比反向迭代器更慢地反转列表

时间:2013-05-09 15:07:19

标签: python

至少有两种方法可以在Python中反转列表,但迭代器方法要快得多(至少在Python 2.7.x中)。我想了解是什么促成了这种速度差异。

>>> x = range(1000)
>>> %timeit x[::-1]
100000 loops, best of 3: 2.99 us per loop
>>> %timeit reversed(x)
10000000 loops, best of 3: 169 ns per loop

我怀疑速度差异至少是由于以下因素:

  1. reversed是用C
  2. 编写的
  3. reversed是一个迭代器,因此内存开销较少
  4. 我尝试使用dis模块来更好地了解这些操作,但这并没有太大帮助。我不得不将这些操作放在一个函数中来反汇编它们。

    >> def reverselist(_list):
    ...     return _list[::-1]
    ... 
    >>> dis.dis(reverselist)
      2           0 LOAD_FAST                0 (_list)
                  3 LOAD_CONST               0 (None)
                  6 LOAD_CONST               0 (None)
                  9 LOAD_CONST               1 (-1)
                 12 BUILD_SLICE              3
                 15 BINARY_SUBSCR       
                 16 RETURN_VALUE
    >>> def reversed_iter(_list):
    ...     return reversed(_list)
    ... 
    >>> dis.dis(reversed_iter)
      2           0 LOAD_GLOBAL              0 (reversed)
                  3 LOAD_FAST                0 (_list)
                  6 CALL_FUNCTION            1
                  9 RETURN_VALUE        
    

    在切片操作期间究竟发生了什么,是否有大量的内存开销?也许切片是用纯Python实现的?

3 个答案:

答案 0 :(得分:10)

那是因为reversed返回iterator,而切片会返回整个列表。

>>> lis = range(10)
>>> lis[::-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> reversed(lis)
<listreverseiterator object at 0x909dd0c>

您必须使用list()将该迭代器转换为整个列表:

>>> lis = range(10**5)
>>> %timeit lis[::-1]
100 loops, best of 3: 2.8 ms per loop
>>> %timeit list(reversed(lis))
100 loops, best of 3: 3.13 ms per loop
reversed上的

帮助

>>> reversed?
Type:       type
String Form:<type 'reversed'>
Namespace:  Python builtin
Docstring:
reversed(sequence) -> reverse iterator over values of the sequence

Return a reverse iterator

答案 1 :(得分:4)

reversed()返回一个迭代器。在你循环之前,它不会实际反转任何东西。来自documentation

  

返回反向 iterator

您需要比较将reversed()的结果再次转换为列表所需的时间:

%timeit list(reversed(x))

只创建迭代器(它只是对原始列表的引用和初始化为列表长度的项指针)不需要任何时间。

必须将reversed()转回列表会使 lot 更慢:

>>> import timeit
>>> x = range(1000)
>>> timeit.timeit('x[::-1]', 'from __main__ import x')
4.623600006103516
>>> timeit.timeit('list(reversed(x))', 'from __main__ import x')
16.647125005722046

答案 2 :(得分:0)

reversed()实际上并没有反转任何东西,它只返回一个对象,可以用来以相反的顺序迭代容器的元素。这就是为什么它比实际反转元素更快的原因。

请注意,您还有reverse()与切片运算符相同。

reverse()运作到位。切片运算符返回一个列表对象。