2016-06-08 112 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的情况下,存在函数调用,所以会产生更多的开销。