2013-01-02 137 views
4

我试图找到列表理解的效率,但它看起来像它比正常的函数操作更昂贵。有人可以解释吗?Python列表理解昂贵

def squares(values): 
    lst = [] 
    for x in range(values): 
     lst.append(x*x) 
    return lst 

def main(): 
    t = timeit.Timer(stmt="lst = [x*x for x in range(10)]") 
    print t.timeit() 
    t = timeit.Timer(stmt="squares",setup="from __main__ import squares") 
    print t.timeit() 

    lst = [x*x for x in range(10)] 
    print lst 
    print squares(10) 



----Output:--- 
2.4147507644 
0.0284455255965 
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 

对于相同的输出,与列表理解相比,正常函数计算的时间很少。

我认为列表理解更有效。

+0

下面的答案解释了您的结果 - 但值得注意的是,为什么列表编辑速度更快 - 循环在较低的级别执行,这意味着它可以更有效地完成。 –

+1

@Lattyware:不,循环实际上没有区别;它是在non-comp版本中调用速度的'.append()'调用。它需要在循环中每次查找和调用,并且每次都为这一个元素增长列表。 comp可以一次构建列表。 –

回答

20

你永远不会致电你的squares函数,所以它没有做任何事情。

列表理解实际上更快:

>>> import timeit 
>>> def squares(values): 
...  lst = [] 
...  for x in range(values): 
...   lst.append(x*x) 
...  return lst 
... 
>>> def squares_comp(values): 
...  return [x*x for x in range(values)] 
... 
>>> timeit.timeit('f(10)', 'from __main__ import squares as f') 
3.9415171146392822 
>>> timeit.timeit('f(10)', 'from __main__ import squares_comp as f') 
2.3243820667266846 

如果使用dis模块来看看字节码每个功能,你可以看到为什么:

>>> import dis 
>>> dis.dis(squares) 
    2   0 BUILD_LIST    0 
       3 STORE_FAST    1 (lst) 

    3   6 SETUP_LOOP    37 (to 46) 
       9 LOAD_GLOBAL    0 (range) 
      12 LOAD_FAST    0 (values) 
      15 CALL_FUNCTION   1 
      18 GET_ITER    
     >> 19 FOR_ITER    23 (to 45) 
      22 STORE_FAST    2 (x) 

    4   25 LOAD_FAST    1 (lst) 
      28 LOAD_ATTR    1 (append) 
      31 LOAD_FAST    2 (x) 
      34 LOAD_FAST    2 (x) 
      37 BINARY_MULTIPLY  
      38 CALL_FUNCTION   1 
      41 POP_TOP    
      42 JUMP_ABSOLUTE   19 
     >> 45 POP_BLOCK   

    5  >> 46 LOAD_FAST    1 (lst) 
      49 RETURN_VALUE   
>>> dis.dis(squares_comp) 
    2   0 BUILD_LIST    0 
       3 LOAD_GLOBAL    0 (range) 
       6 LOAD_FAST    0 (values) 
       9 CALL_FUNCTION   1 
      12 GET_ITER    
     >> 13 FOR_ITER    16 (to 32) 
      16 STORE_FAST    1 (x) 
      19 LOAD_FAST    1 (x) 
      22 LOAD_FAST    1 (x) 
      25 BINARY_MULTIPLY  
      26 LIST_APPEND    2 
      29 JUMP_ABSOLUTE   13 
     >> 32 RETURN_VALUE   

squares功能在每次迭代中查找列表的.append()方法,并调用它。每次调用时,.append()函数必须将列表增加一个元素。

另一方面,列表理解不需要做那项工作。相反,python使用LIST_APPEND字节码,它使用C API将新元素附加到列表中,而无需对函数执行查找和python调用。

+0

+1,打我吧。 –

+0

谁应该奖励我的投票呢?这很难选择:( –

+3

@ SamuelleMattiuzzo我删除了我的(没有点有两次相同的事情),所以在这一票前面投票 –