2016-06-08 88 views
1

創建列表時,我認爲只要可能,建議理解最快。但是,你看。爲什麼列表乘法這麼快?

In [1]: %timeit -n1000 [0]*1000000 
1000 loops, best of 3: 2.3 ms per loop 

In [2]: %timeit -n1000 [0 for _ in range(1000000)] 
1000 loops, best of 3: 27.1 ms per loop 

In [3]: a = np.zeros(1000000, dtype=int) 

In [4]: %timeit -n1000 a.tolist() 
1000 loops, best of 3: 7.93 ms per loop 

即使是numpy.ndarray.tolist也跟不上乘法。這是爲什麼?

+0

#2實際運行一個python循環,而#1完全沒有任何python循環。 – spectras

回答

3

dis模塊可用於比較前兩種方法。

def list_mult(): 
    return [0]*1000000 

dis.dis(list_mult) 
# 2   0 LOAD_CONST    1 (0) 
#    3 BUILD_LIST    1 
#    6 LOAD_CONST    2 (1000000) 
#    9 BINARY_MULTIPLY  
#    10 RETURN_VALUE   

這裏使用BINARY_MULTIPLY指令。另一方面...

def list_range(): 
    return [0 for _ in range(1000000)] 

dis.dis(list_range) 
# 2   0 BUILD_LIST    0 
#    3 LOAD_GLOBAL    0 (range) 
#    6 LOAD_CONST    1 (1000000) 
#    9 CALL_FUNCTION   1 
#   12 GET_ITER    
#  >> 13 FOR_ITER    12 (to 28) 
#   16 STORE_FAST    0 (_) 
#   19 LOAD_CONST    2 (0) 
#   22 LIST_APPEND    2 
#   25 JUMP_ABSOLUTE   13 
#  >> 28 RETURN_VALUE  

此函數顯式構造一個循環,然後加載0並將其附加到每個迭代中的工作列表。這將會慢很多。

應當指出的是,這兩種施工方法是不等價的,特別是當列表中的數值是可變的。例如,[object()] * 10將爲您提供同一個對象的10個列表,而[object() for _ in range(10)]將爲您提供10個不同對象的列表。

關於numpy的例子,這個操作是numpy的最壞情況。構建和轉換numpy數組有很多開銷,所以矢量化操作可以很快(如註釋中所述)。

0

在第一種情況下,python可以創建一個列表,其中所有的零都指向相同的id。這很有用,因爲原始文字是,文字爲,但如果傳遞一個對象,它不會按預期工作。在這種情況下,每個元素實際上都是對同一個對象的引用。

range的情況下,存在函數調用,所以會產生更多的開銷。