为什么列表理解如此之快?
这个问题在这里已经有了答案:
列表推导在这里执行得更好,因为您不需要加载列表的append属性并将其作为函数调用!*
考虑下面的例子:
>>> import dis
>>> def f1():
... for i in range(5):
... l.append(i)
...
>>> def f2():
... [i for i in range(5)]
...
>>> dis.dis(f1)
2 0 SETUP_LOOP 33 (to 36)
3 LOAD_GLOBAL 0 (range)
6 LOAD_CONST 1 (5)
9 CALL_FUNCTION 1
12 GET_ITER
>> 13 FOR_ITER 19 (to 35)
16 STORE_FAST 0 (i)
3 19 LOAD_GLOBAL 1 (l)
22 LOAD_ATTR 2 (append)
25 LOAD_FAST 0 (i)
28 CALL_FUNCTION 1
31 POP_TOP
32 JUMP_ABSOLUTE 13
>> 35 POP_BLOCK
>> 36 LOAD_CONST 0 (None)
39 RETURN_VALUE
>>> dis.dis(f2)
2 0 BUILD_LIST 0
3 LOAD_GLOBAL 0 (range)
6 LOAD_CONST 1 (5)
9 CALL_FUNCTION 1
12 GET_ITER
>> 13 FOR_ITER 12 (to 28)
16 STORE_FAST 0 (i)
19 LOAD_FAST 0 (i)
22 LIST_APPEND 2
25 JUMP_ABSOLUTE 13
>> 28 POP_TOP
29 LOAD_CONST 0 (None)
32 RETURN_VALUE
>>>
你可以看到,在字节码22中,我们在第一个函数中有一个append
属性,因为在使用列表理解的第二个函数中没有这样的事情。
还要注意,你将在每次迭代中append
attr加载,因此它使你的代码比使用列表理解的第二个函数慢大约2倍。
此外,根据您的Python和代码,列表内涵可能会遇到比手动更快for
,因为他们的迭代解释在里面C语言的速度进行 ,而不是手动Python代码循环语句(通常大约快两倍)。 特别是对于较大的数据集,使用此表达式通常具有主要的性能优势。**
*你可以阅读更多关于列表理解的效率
** Mark Lutz学习Python
即使不考虑查找和加载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)
引用这篇文章,是因为list
的append
属性没有被查找,加载并作为一个函数被调用,这需要时间并且会叠加迭代。