我期待在像在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/ –
嗯,我總是使用列表解析,即使我完全拋棄結果... –
@約翰科爾曼:/請這樣做,併發佈一個答案... –