[英]Why is a list comprehension so much faster than appending to a list?
我想知道為什么列表理解比附加到列表要快得多。 我認為差異只是表現力,但事實並非如此。
>>> import timeit
>>> timeit.timeit(stmt='''\
t = []
for i in range(10000):
t.append(i)''', number=10000)
9.467898777974142
>>> timeit.timeit(stmt='t= [i for i in range(10000)]', number=10000)
4.1138417314859
列表理解速度提高了 50%。 為什么?
列表理解基本上只是常規for
循環的“語法糖”。 在這種情況下,它表現更好的原因是它不需要加載列表的 append 屬性並在每次迭代時將其作為函數調用。 換句話說,一般而言,列表推導式執行得更快,因為掛起和恢復函數的框架或其他情況下的多個函數比按需創建列表慢。
考慮以下示例:
In [1]: def f1():
...: l = []
...: for i in range(5):
...: l.append(i)
...:
...:
...: def f2():
...: [i for i in range(5)]
...:
In [3]: import dis
In [4]: dis.dis(f1)
2 0 BUILD_LIST 0
2 STORE_FAST 0 (l)
3 4 LOAD_GLOBAL 0 (range)
6 LOAD_CONST 1 (5)
8 CALL_FUNCTION 1
10 GET_ITER
>> 12 FOR_ITER 14 (to 28)
14 STORE_FAST 1 (i)
4 16 LOAD_FAST 0 (l)
18 LOAD_METHOD 1 (append)
20 LOAD_FAST 1 (i)
22 CALL_METHOD 1
24 POP_TOP
26 JUMP_ABSOLUTE 12
>> 28 LOAD_CONST 0 (None)
30 RETURN_VALUE
In [5]:
In [5]: dis.dis(f2)
8 0 LOAD_CONST 1 (<code object <listcomp> at 0x7f397abc0d40, file "<ipython-input-1-45c11e415ee9>", line 8>)
2 LOAD_CONST 2 ('f2.<locals>.<listcomp>')
4 MAKE_FUNCTION 0
6 LOAD_GLOBAL 0 (range)
8 LOAD_CONST 3 (5)
10 CALL_FUNCTION 1
12 GET_ITER
14 CALL_FUNCTION 1
16 POP_TOP
18 LOAD_CONST 0 (None)
20 RETURN_VALUE
Disassembly of <code object <listcomp> at 0x7f397abc0d40, file "<ipython-input-1-45c11e415ee9>", line 8>:
8 0 BUILD_LIST 0
2 LOAD_FAST 0 (.0)
>> 4 FOR_ITER 8 (to 14)
6 STORE_FAST 1 (i)
8 LOAD_FAST 1 (i)
10 LIST_APPEND 2
12 JUMP_ABSOLUTE 4
>> 14 RETURN_VALUE
In [6]:
您可以看到,在第一個函數中的偏移量 18 處,我們有一個append
屬性,而在使用列表推導式的第二個函數中沒有這樣的東西。 所有這些額外的字節碼都會使追加方法變慢,因為在這種情況下,您將在每次迭代中加載append
屬性,最終它會使代碼比僅使用列表理解的第二個函數慢大約兩倍.
即使將查找和加載append
函數所需的時間計算append
,列表理解仍然更快,因為列表是用 C 創建的,而不是在 Python 中一次構建一個項目。
# Slow
timeit.timeit(stmt='''
for i in range(10000):
t.append(i)''', setup='t=[]', number=10000)
# Faster
timeit.timeit(stmt='''
for i in range(10000):
l(i)''', setup='t=[]; l=t.append', number=10000)
# Faster still
timeit.timeit(stmt='t = [i for i in range(10000)]', number=10000)
引用這篇文章,這是因為list
的append
屬性不是作為函數查找、加載和調用的,這需要時間並且會在迭代中累加。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.