当前位置: 首页 > 知识库问答 >
问题:

为什么列表理解比附加到列表中要快得多?

国兴贤
2023-03-14

我想知道为什么列表理解比附加在列表上要快得多。我以为区别只是表达,但事实并非如此。

>>> import timeit 
>>> timeit.timeit(stmt='''\
t = []
for i in range(10000):
    t.append(i)''', number=10000)
9.467898777974142

>>> timeit.timeit(stmt='t= [i for i in range(10000)]', number=10000)
4.1138417314859

列表理解速度提高了50%。为什么?

共有3个答案

酆耀
2023-03-14

引用这篇文章,是因为listappend属性没有作为函数进行查找、加载和调用,这需要时间,并且需要迭代。

龙安阳
2023-03-14

即使考虑到查找和加载append函数所需的时间,列表理解仍然更快,因为列表是用C语言创建的,而不是用Python一次构建一个项目。

# Slow
timeit.timeit(stmt='''
    for i in range(10000):
        t.append(i)''', setup='t=[]', number=10000)

# Faster
timeit.timeit(stmt='''
    for i in range(10000):
        l(i)''', setup='t=[]; l=t.append', number=10000)

# Faster still
timeit.timeit(stmt='t = [i for i in range(10000)]', number=10000)
吕宇定
2023-03-14

列表理解基本上只是常规for循环的“语法糖”。在这种情况下,它表现更好的原因是因为它不需要加载列表的append属性,并在每次迭代时作为函数调用它。换句话说,总的来说,列表理解表现得更快,因为暂停和恢复一个函数的框架,或者在其他情况下多个函数,比按需创建列表要慢。

考虑以下例子:

In [1]: def f1(): 
   ...:         l = [] 
   ...:         for i in range(5): 
   ...:             l.append(i) 
   ...:     
   ...:  
   ...: def f2(): 
   ...:     [i for i in range(5)] 
   ...:                                                                                                                                                                                                     

In [3]: import dis                                                                                                                                                                                          

In [4]: dis.dis(f1)                                                                                                                                                                                         
  2           0 BUILD_LIST               0
              2 STORE_FAST               0 (l)

  3           4 LOAD_GLOBAL              0 (range)
              6 LOAD_CONST               1 (5)
              8 CALL_FUNCTION            1
             10 GET_ITER
        >>   12 FOR_ITER                14 (to 28)
             14 STORE_FAST               1 (i)

  4          16 LOAD_FAST                0 (l)
             18 LOAD_METHOD              1 (append)
             20 LOAD_FAST                1 (i)
             22 CALL_METHOD              1
             24 POP_TOP
             26 JUMP_ABSOLUTE           12
        >>   28 LOAD_CONST               0 (None)
             30 RETURN_VALUE

In [5]:                                                                                                                                                                                                     

In [5]: dis.dis(f2)                                                                                                                                                                                         
  8           0 LOAD_CONST               1 (<code object <listcomp> at 0x7f397abc0d40, file "<ipython-input-1-45c11e415ee9>", line 8>)
              2 LOAD_CONST               2 ('f2.<locals>.<listcomp>')
              4 MAKE_FUNCTION            0
              6 LOAD_GLOBAL              0 (range)
              8 LOAD_CONST               3 (5)
             10 CALL_FUNCTION            1
             12 GET_ITER
             14 CALL_FUNCTION            1
             16 POP_TOP
             18 LOAD_CONST               0 (None)
             20 RETURN_VALUE

Disassembly of <code object <listcomp> at 0x7f397abc0d40, file "<ipython-input-1-45c11e415ee9>", line 8>:
  8           0 BUILD_LIST               0
              2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                 8 (to 14)
              6 STORE_FAST               1 (i)
              8 LOAD_FAST                1 (i)
             10 LIST_APPEND              2
             12 JUMP_ABSOLUTE            4
        >>   14 RETURN_VALUE

In [6]:   

您可以看到,在第一个函数的偏移量18上,我们有一个append属性,而在第二个函数中,使用列表理解没有这样的东西。所有这些额外的字节码都会使附加方法变得更慢,因为在这种情况下,每次迭代都会加载append属性,最终只使用列表理解,它会使代码比第二个函数慢大约两倍。

 类似资料:
  • 问题内容: 一些用户建议使用numpy的以下方法,以及我认为是正确的方法: 我也发现具有相同的性能。无论如何,另一个答案建议使用列表理解的解决方案: 但是在基准测试之后,我发现列表理解比numpy快得多: 结果: 如您所见,numpy大约快5倍。但最令人惊讶的是,它无需使用转置就可以更快地运行,并且适用于以下代码: 列表理解仍然快了5倍。因此,除了这一点之外,这里的列表理解是在C语言中执行的,我们

  • 问题内容: 这个问题不太可能对将来的访客有所帮助;它仅与较小的地理区域,特定的时间段或极为狭窄的情况相关,通常不适用于Internet的全球受众。要获得使该问题更广泛适用的帮助,请访问帮助中心。 8年前关闭。 PHP有一种非常快速的方法将值附加到数组: 在python中不需要索引号的最简单方法是什么?另外,有没有一种简单的方法可以使用嵌套在字典中的列表来执行此操作,例如此PHP等效项。 问题答案:

  • 问题内容: 我刚刚读过“深入Python”,“元组比列表快”。 元组是不可变的,列表是可变的,但是我不太明白为什么元组更快。 有人对此进行过性能测试吗? 问题答案: 所报告的“构建速度”比率仅适用于 常量 元组(其项目由文字表示的元组)。仔细观察(并在您的机器上重复-您只需要在shell /命令窗口中键入命令即可!)…: 我没有在3.0上进行测量,因为我当然没有它-它已经完全过时了,绝对没有理由保

  • 问题内容: 这似乎是有效的和太。 这就是我的意思: 在列表样式中,我指的是实际的“项目符号”或“数字”(使用时) 问题答案: 这是因为通常将设置为for 元素。请参阅W3CCSS3规范: 要声明列表项,应将“ display”属性设置为“ list-item”。 请注意,您可以为任意HTML元素提供相同的行为。设置在例如。

  • 问题内容: 阅读有关ADT列表的Java文档时,它说: List接口提供了四种位置(索引)访问列表元素的方法。列表(如Java数组)从零开始。请注意,对于某些实现(例如,LinkedList类),这些操作可能在时间上与索引值成比例执行。因此,如果调用者不知道实现,则遍历列表中的元素通常比对其进行索引更可取。 这到底是什么意思?我不明白得出的结论。 问题答案: 在链接列表中,每个元素都有一个指向下一

  • 我一直在玩Java 8 ,我决定对 和 流进行微基准测试。正如预期的那样, 的速度是原来的两倍,但还是出现了其他一些问题--如果我在将数据传递给 之前先对其进行排序,则与传递未排序列表相比, Map->Collect/code>得到结果所需的时间要多出5-8倍。 下面是一个更好的基准测试代码 结果也是相似的: 那么,我的问题是为什么过滤一个未排序的列表比过滤一个已排序的列表更快呢?