Python 为什么列表理解比附加到列表快得多?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/30245397/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why is a list comprehension so much faster than appending to a list?
提问by rafaelc
I was wondering why list comprehension is so much faster than appending to a list. I thought the difference is just expressive, but it's not.
我想知道为什么列表理解比附加到列表要快得多。我认为差异只是表现力,但事实并非如此。
>>> 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
The list comprehension is 50% faster. Why?
列表理解速度提高了 50%。为什么?
采纳答案by Kasramvd
List comprehensionis basically just a "syntactic sugar" for the regular for
loop. In this case the reason that it performs better is because it doesn't need to load the append attribute of the list and call it as a function at each iteration. In other words and in general, list comprehensions perform faster because suspending and resuming a function's frame, or multiple functions in other cases, is slower than creating a list on demand.
列表理解基本上只是常规for
循环的“语法糖” 。在这种情况下,它表现更好的原因是它不需要加载列表的 append 属性并在每次迭代时将其作为函数调用。换句话说,一般来说,列表推导式执行得更快,因为挂起和恢复函数的框架,或在其他情况下的多个函数,比按需创建列表慢。
Consider the following examples :
考虑以下示例:
# Python-3.6
In [1]: import dis
In [2]: def f1():
...: l = []
...: for i in range(5):
...: l.append(i)
...:
In [3]: def f2():
...: [i for i in range(5)]
...:
In [4]: dis.dis(f1)
2 0 BUILD_LIST 0
3 STORE_FAST 0 (l)
3 6 SETUP_LOOP 33 (to 42)
9 LOAD_GLOBAL 0 (range)
12 LOAD_CONST 1 (5)
15 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
18 GET_ITER
>> 19 FOR_ITER 19 (to 41)
22 STORE_FAST 1 (i)
4 25 LOAD_FAST 0 (l)
28 LOAD_ATTR 1 (append)
31 LOAD_FAST 1 (i)
34 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
37 POP_TOP
38 JUMP_ABSOLUTE 19
>> 41 POP_BLOCK
>> 42 LOAD_CONST 0 (None)
45 RETURN_VALUE
In [5]: dis.dis(f2)
2 0 LOAD_CONST 1 (<code object <listcomp> at 0x7fe48b2265d0, file "<ipython-input-3-9bc091d521d5>", line 2>)
3 LOAD_CONST 2 ('f2.<locals>.<listcomp>')
6 MAKE_FUNCTION 0
9 LOAD_GLOBAL 0 (range)
12 LOAD_CONST 3 (5)
15 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
18 GET_ITER
19 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
22 POP_TOP
23 LOAD_CONST 0 (None)
26 RETURN_VALUE
You can see at offset 22 we have an append
attribute in first function since we don't have such thing in second function using list comprehension. All those extra bytecodes will make the appending approach slower. Also note that you'll also have the append
attribute loading in each iteration which makes your code takes approximately 2 time slower than the second function using list comprehension.
您可以看到在偏移量 22 处,我们append
在第一个函数中有一个属性,因为我们在使用列表理解的第二个函数中没有这样的东西。所有这些额外的字节码都会使附加方法变慢。另请注意,您还将append
在每次迭代中加载属性,这使您的代码比使用列表理解的第二个函数慢大约 2 倍。
回答by adarsh
回答by chepner
Even factoring out the time it takes to lookup and load the append
function, the list comprehension is still faster because the list is created in C, rather than built up one item at a time in Python.
即使考虑到查找和加载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)