Python列表()与列表推导式构建速度对比

23

这很有趣; 使用list()强制将迭代器转换为实际列表的速度比使用推导式[x for x in someList]快得多。

这是真实情况还是我的测试太简单了?以下是代码:

import time    

timer = time.clock()
for i in xrange(90):

    #localList = [x for x in xrange(1000000)]   #Very slow, took me 6.8s
    localList = list(xrange(1000000))           #Very fast, took me 0.9s

    print localList[999999] #make sure list is really evaluated.

print "Total time: ", time.clock() - timer

Python 3.4在一个包含10^6个元素的列表上使用timeit计时得到了5.5秒,而Python 3.6则只用了3.6秒。 - user
@user5061:如果将其封装在一个函数中并使用本地变量,这里的时间会更好。在Python 3中,列表推导式具有自己的作用域(就像生成器表达式一样),并且本地变量更快。 - Martijn Pieters
2个回答

24

列表推导式和普通的 for 循环一样,会在Python字节码中执行循环。

list() 函数的调用完全在C代码中迭代,因此速度更快。

列表推导式的字节码如下所示:

>>> import dis
>>> dis.dis(compile("[x for x in xrange(1000000)]", '<stdin>', 'exec'))
  1           0 BUILD_LIST               0
              3 LOAD_NAME                0 (xrange)
              6 LOAD_CONST               0 (1000000)
              9 CALL_FUNCTION            1
             12 GET_ITER            
        >>   13 FOR_ITER                12 (to 28)
             16 STORE_NAME               1 (x)
             19 LOAD_NAME                1 (x)
             22 LIST_APPEND              2
             25 JUMP_ABSOLUTE           13
        >>   28 POP_TOP             
             29 LOAD_CONST               1 (None)
             32 RETURN_VALUE        

>> 指针大致给出了正在执行的循环的边界,因此您需要在 Python 字节码评估循环中执行 1000000 次 STORE_NAMELOAD_NAMELIST_APPEND 步骤。

另一方面,list() 仅通过使用对象迭代的 C API 直接获取来自 xrange() 可迭代对象的值,并且可以使用 xrange() 对象的长度来预分配列表对象,而不是动态增长该对象。


这只是一个实现细节吗?编译器是否可以在任何时候自动转换列表推导式,只要它以那种形式出现? - Karoly Horvath
@KarolyHorvath:把它转化成什么? - Martijn Pieters
转换为快速版本:从 [v for v in iterable] 形式转换为 list(iterable) 形式(当然要使用内置的 list,以防它在本地被定义)。 - Karoly Horvath
@KarolyHorvath:可能可以;我目前想不到为什么表单不能被list()替换的理由。我经常看到这种错误(例如rows = [row for row in csvreader]lines = [line for line in fileobj]等)。我会考虑在python-ideas列表中发布这个问题。 - Martijn Pieters
我相信同样的情况也会出现在dict()和{d:True for d in someList}之间。那么初始化这种字典的最佳方式是什么? - Cristiano Coelho
1
@user1777914:dict.fromkeys(someList, True) - Martijn Pieters

-2

这似乎不再成立了(我检查了Python 3.7-3.10)。 列表推导比列表函数更快。 我听说在3.10和3.11中也有加速。


2
请问您能否发布一些数字吗? %timeit l=[x for x in range(1000000)] => 22.8 ms ± 207 µs per loop%timeit l=list(range(1000000)) => 12.1 ms ± 176 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) (python 3.9.12) - minusf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接