创建列表时,我认为尽可能推荐理解,因为它最快。但是,请看。
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 :(得分: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数组会有很多开销,因此矢量化操作可以很快(如注释中所述)。
答案 1 :(得分:0)
在第一种情况下,python可以创建一个列表,其中所有的零引用相同的id
。这很有效,因为原语是 literal ,但是如果你传递一个对象,它将无法正常工作。在这种情况下,每个元素实际上都是对同一对象的引用。
在range
的情况下,正在进行函数调用,因此会产生更多的开销。