我期待在像在Python结构环路的性能问题,并找到下面的语句:为什么在Python中列表的理解速度可能比map()更快?
除了列表解析的语法利益,他们往往 一样快或比同等使用地图的速度更快。 (Performance Tips)
列表解析运行速度比等效的for-loops快一点(除非 你只是要扔掉结果)。 (Python Speed)
我想知道在引擎盖下有什么区别给出列表理解这一优势。谢谢。
我期待在像在Python结构环路的性能问题,并找到下面的语句:为什么在Python中列表的理解速度可能比map()更快?
除了列表解析的语法利益,他们往往 一样快或比同等使用地图的速度更快。 (Performance Tips)
列表解析运行速度比等效的for-loops快一点(除非 你只是要扔掉结果)。 (Python Speed)
我想知道在引擎盖下有什么区别给出列表理解这一优势。谢谢。
测试一:扔掉结果。
这里是我们的虚拟函数:
def examplefunc(x):
pass
这里是我们的挑战者:
def listcomp_throwaway():
[examplefunc(i) for i in range(100)]
def forloop_throwaway():
for i in range(100):
examplefunc(i)
我不会做它的原始速度的分析,只有为什么,每任择议定书的问题。让我们看看机器代码的差异。
--- List comprehension
+++ For loop
@@ -1,15 +1,16 @@
- 55 0 BUILD_LIST 0
+ 59 0 SETUP_LOOP 30 (to 33)
3 LOAD_GLOBAL 0 (range)
6 LOAD_CONST 1 (100)
9 CALL_FUNCTION 1
12 GET_ITER
- >> 13 FOR_ITER 18 (to 34)
+ >> 13 FOR_ITER 16 (to 32)
16 STORE_FAST 0 (i)
- 19 LOAD_GLOBAL 1 (examplefunc)
+
+ 60 19 LOAD_GLOBAL 1 (examplefunc)
22 LOAD_FAST 0 (i)
25 CALL_FUNCTION 1
- 28 LIST_APPEND 2
- 31 JUMP_ABSOLUTE 13
- >> 34 POP_TOP
- 35 LOAD_CONST 0 (None)
- 38 RETURN_VALUE
+ 28 POP_TOP
+ 29 JUMP_ABSOLUTE 13
+ >> 32 POP_BLOCK
+ >> 33 LOAD_CONST 0 (None)
+ 36 RETURN_VALUE
比赛开始。 Listcomp的第一步是构建一个空列表,而for循环则是设置一个循环。他们两个然后继续加载全局范围(),常量100,并调用生成器的范围函数。然后他们都获得当前的迭代器并获取下一个项目,并将其存储到变量i中。然后他们加载examplefunc和我并调用examplefunc。 Listcomp将它追加到列表中,并再次启动循环。 For循环在三条指令中执行相同的操作,而不是两条。然后它们都加载None并将其返回。
那么在这个分析中谁更好?在这里,列表理解会执行一些冗余操作,例如构建列表并追加到列表中,如果您不关心结果。 For循环也非常有效。
如果你计时,使用for循环大约比列表理解速度快三分之一。 (在这个测试中,examplefunc将它的参数划分为5并丢弃它,而不是什么也不做。)
测试二:保持结果一样正常。
这个测试没有虚拟函数。所以这里是我们的挑战者:
def listcomp_normal():
l = [i*5 for i in range(100)]
def forloop_normal():
l = []
for i in range(100):
l.append(i*5)
这个差异对我们今天没有任何用处。这只是两个块中的两个机器码。
名单补偿的机器代码:
55 0 BUILD_LIST 0
3 LOAD_GLOBAL 0 (range)
6 LOAD_CONST 1 (100)
9 CALL_FUNCTION 1
12 GET_ITER
>> 13 FOR_ITER 16 (to 32)
16 STORE_FAST 0 (i)
19 LOAD_FAST 0 (i)
22 LOAD_CONST 2 (5)
25 BINARY_MULTIPLY
26 LIST_APPEND 2
29 JUMP_ABSOLUTE 13
>> 32 STORE_FAST 1 (l)
35 LOAD_CONST 0 (None)
38 RETURN_VALUE
For循环的机器代码:
59 0 BUILD_LIST 0
3 STORE_FAST 0 (l)
60 6 SETUP_LOOP 37 (to 46)
9 LOAD_GLOBAL 0 (range)
12 LOAD_CONST 1 (100)
15 CALL_FUNCTION 1
18 GET_ITER
>> 19 FOR_ITER 23 (to 45)
22 STORE_FAST 1 (i)
61 25 LOAD_FAST 0 (l)
28 LOAD_ATTR 1 (append)
31 LOAD_FAST 1 (i)
34 LOAD_CONST 2 (5)
37 BINARY_MULTIPLY
38 CALL_FUNCTION 1
41 POP_TOP
42 JUMP_ABSOLUTE 19
>> 45 POP_BLOCK
>> 46 LOAD_CONST 0 (None)
49 RETURN_VALUE
正如你可能已经知道,在列表理解比for循环做较少的指令。
列表理解的清单:
range
。100
。range
。i
。i
。l
到匿名空白列表。For循环的清单:
l
到匿名空白列表。range
。100
。range
。i
。l
。append
。i
。append
。(不包括下列步骤:加载None
,其返回。)
列表内涵没有做这些事情:
i
两次最后的包装,listcomp是快了很多,如果你将会使用这些值,但是如果你不这样做,它会很慢。
真实速度
测试一个:for循环是由约三分之一*更快
测试二:列表理解是大约三分之二*
*关于快 - >小数点后第二位鼓励
非常感谢您的分析! – Bin
@Bin谢谢!我也帮助自己,所以双赢! –
http://blog.codinghorror.com/learn-to-read-the-source-luke/ –
嗯,我总是使用列表解析,即使我完全抛弃结果... –
@约翰科尔曼:/请这样做,并发布一个答案... –