【问题标题】:Python list comprehension expensivePython列表理解昂贵
【发布时间】:2012-12-16 23:18:39
【问题描述】:

我试图找到列表理解的效率,但它看起来比普通函数操作更昂贵。谁能解释一下?

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]

对于相同的输出,与列表推导相比,普通函数的计算时间非常短。

我认为列表理解更有效。

【问题讨论】:

  • 下面的答案解释了你的结果 - 但值得注意的是为什么列表 comp 更快 - 循环在较低级别执行,这意味着它可以更有效地完成。
  • @Lattyware:不,循环实际上没有区别;非压缩版本中的.append() 调用加快了速度。每次都需要在循环中查找和调​​用它,并且每次都会为那个元素增长列表。 comp 可以一口气建立列表。

标签: python list-comprehension


【解决方案1】:

你从来没有调用你的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 调用。

【讨论】:

  • @SamueleMattiuzzo 我删除了我的(两次重复相同的东西没有意义),所以投票给这个。
  • @SamueleMattiuzzo - 现在不难选择了 :-)
  • AFAIK 列表理解更快,因为操作是并行完成的
  • @toutpt:你从哪里得到那个的想法?它不会并行执行任何操作。
  • @JonClements:使用def squares_map(values):\n values=range(values)\n return map(operator.mul, values, values)\n,它的竞争力在 2.5397000312805176。列表组合仍然获胜。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-02-20
  • 1970-01-01
  • 1970-01-01
  • 2011-12-06
  • 1970-01-01
  • 2018-03-27
  • 1970-01-01
相关资源
最近更新 更多