为什么 .append() 比在预分配数组中设置值慢?
我正在尝试加快我的代码,其中有一部分需要循环遍历并设置一个很大的二维数组的值。有人建议我可以先分配好数组的空间,而不是使用.append()方法,但有人指出在Python中,.append()的平均时间复杂度是O(1)。
不过,当我用以下代码进行测试时:
import time
x = list()
z = list()
t1 = time.time()
for i in range(10000):
z.append([])
for j in range(10000):
z[i].append(0)
t1 = time.time()
for i in range(10000):
x.append([])
for j in range(10000):
x[i].append(1)
print(time.time()-t1)
t1 = time.time()
for i in range(10000):
for j in range(10000):
z[i][j] = 1
print(time.time()-t1)
我发现预先分配好的数组总是比没有预先分配的数组快3到4秒(大约17秒对比21秒)。那么在这段代码中,是什么原因导致使用.append()的方法比在预先分配的数组中替换值要慢呢?
2 个回答
2
.append() 方法可能会导致 Python 分配更多的内存,这个过程是需要时间的。如果你使用预先分配好的结构,就能节省每次单独分配内存所花费的时间。
7
考虑以下内容:
from dis import dis
def f1():
x = []
for i in range(10000):
x.append([])
for j in range(10000):
x[i].append(0)
return x
dis(f1)
2 0 BUILD_LIST 0
3 STORE_FAST 0 (x)
3 6 SETUP_LOOP 73 (to 82)
9 LOAD_GLOBAL 0 (range)
12 LOAD_CONST 1 (10000)
15 CALL_FUNCTION 1
18 GET_ITER
>> 19 FOR_ITER 59 (to 81)
22 STORE_FAST 1 (i)
4 25 LOAD_FAST 0 (x)
28 LOAD_ATTR 1 (append)
31 BUILD_LIST 0
34 CALL_FUNCTION 1
37 POP_TOP
5 38 SETUP_LOOP 37 (to 78)
41 LOAD_GLOBAL 0 (range)
44 LOAD_CONST 1 (10000)
47 CALL_FUNCTION 1
50 GET_ITER
>> 51 FOR_ITER 23 (to 77)
54 STORE_FAST 2 (j)
6 57 LOAD_FAST 0 (x)
60 LOAD_FAST 1 (i)
63 BINARY_SUBSCR
64 LOAD_ATTR 1 (append)
67 LOAD_CONST 2 (0)
70 CALL_FUNCTION 1
73 POP_TOP
74 JUMP_ABSOLUTE 51
>> 77 POP_BLOCK
>> 78 JUMP_ABSOLUTE 19
>> 81 POP_BLOCK
7 >> 82 LOAD_FAST 0 (x)
85 RETURN_VALUE
与之相比:
def f2():
x = list()
for i in range(10000):
x.append([0]*10000)
return x
dis(f2)
2 0 LOAD_GLOBAL 0 (list)
3 CALL_FUNCTION 0
6 STORE_FAST 0 (x)
3 9 SETUP_LOOP 40 (to 52)
12 LOAD_GLOBAL 1 (range)
15 LOAD_CONST 1 (10000)
18 CALL_FUNCTION 1
21 GET_ITER
>> 22 FOR_ITER 26 (to 51)
25 STORE_FAST 1 (i)
4 28 LOAD_FAST 0 (x)
31 LOAD_ATTR 2 (append)
34 LOAD_CONST 2 (0)
37 BUILD_LIST 1
40 LOAD_CONST 1 (10000)
43 BINARY_MULTIPLY
44 CALL_FUNCTION 1
47 POP_TOP
48 JUMP_ABSOLUTE 22
>> 51 POP_BLOCK
5 >> 52 LOAD_FAST 0 (x)
55 RETURN_VALUE
你处理事情的方式可能会带来很大的不同。