为什么list(xrange)比range()慢?

时间:2013-04-27 13:22:28

标签: python performance python-2.7

我正在进行几项测试,发现xrange()range()快得多(正如各种问题/答案所证实的那样):

>>> from timeit import timeit
>>> timeit(stmt = 'x = range(1000)', number = 10000)
0.38216601211680734
>>> timeit(stmt = 'x = xrange(1000)', number = 10000)
0.010537726631953959 # xrange is much faster than range

我很好奇,所以我尝试了另一项测试,看看list(xrange(1000))是否仍然比单纯range(1000)更快:

>>> timeit(stmt = 'x = range(1000)', number = 10000)
0.3858838963796529
>>> timeit(stmt = 'x = list(xrange(1000))', number = 10000)
0.492734766028903 # now, xrange is slower

更多来电也是如此:

>>> timeit(stmt = 'x = range(1000)', number = 100000)
3.6457308233315757
>>> timeit(stmt = 'x = list(xrange(1000))', number = 100000)
5.722031755612818

所以,我的问题是,为什么list(xrange)明显慢于range本身?

我看到了this question on the slowness of list(), dict(), and other constructor methods,那么为什么list(xrange)会慢得多?

使用dis.dis(),我发现list(xrange)执行的计算次数多于range):

>>> dis.dis('x = list(xrange(1000))')
          0 SETUP_LOOP      15648 (to 15651)
          3 SLICE+2        
          4 IMPORT_NAME     29545 (29545)
          7 LOAD_GLOBAL     30760 (30760)
         10 POP_JUMP_IF_FALSE 28257
         13 BUILD_LIST      10341
         16 <49>           
         17 <48>           
         18 <48>           
         19 <48>           
         20 STORE_SLICE+1  
         21 STORE_SLICE+1  
>>> dis.dis('x = range(1000)')
          0 SETUP_LOOP      15648 (to 15651)
          3 SLICE+2        
          4 POP_JUMP_IF_FALSE 28257
          7 BUILD_LIST      10341
         10 <49>           
         11 <48>           
         12 <48>           
         13 <48>           
         14 STORE_SLICE+1  

2 个答案:

答案 0 :(得分:6)

当然range()会更快,当你想要的最终产品是一个范围内所有数字的列表时,range在一个函数调用中完成所有这些。与list(xrange())相反,list(..)使rangeiterator构造函数的负担增加xrange对象的开销,以迭代list(..)对象,range()构造函数必须消耗。而{{1}}立即构造列表,没有中间迭代器消耗......怎么可能被打败?主要区别在于:1个函数调用vs 2,不太重要的是1个全局查找vs 2。

答案 1 :(得分:2)

range()会立即构建一个列表并将其返回给您。因此,如果它有很多项目,那么构建起来会很慢。

另一方面,xrange()直接返回类似迭代器的对象,当您请求它们时会产生值。来自文档:

  

xrange(开始,停止[,步骤])
  此函数与range()非常相似,但返回 xrange 对象而不是列表。这是一个不透明的序列类型,它产生与相应列表相同的值,而不是实际同时存储它们。 xrange()优于range()的优势很小(因为xrange()仍需要在被要求时创建值)除了,当使用非常大的范围时一个内存不足的机器或者从不使用所有范围的元素时(例如当循环通常以中断结束时)。

所以,在你的第一个例子中,xrange()自然更快,而在第二个例子中它更慢,因为虽然range()已经返回一个列表(当时不会被转换),{{ 1}}必须做更多的工作,不仅要产生价值,还要创建一个新的列表并存储价值。

P.S。这适用于Python 2.在Python 3中,只有list(xrange())与Python 2完全相同 - range()

相关问题