Python list comprehension 和 append 性能对比

最近有被问list comprehension 和一个列表不停地append哪个效率高,为什么?

表示没关注过(so sad),因为感觉list comprehension在可读性上并不好,
而且也无法在遍历时候捕获异常,仅仅适合简单的场景。

测试1,是否list comprehension就比append快:

def test():
    """Stupid test function"""
    L = []
    for i in range(100):
        L.append(i*2)
    return L

def test2():
    return [i * 2 for i in range(100)]

if __name__ == '__main__':
    import timeit
    print(timeit.timeit("test()", setup="from __main__ import test"))
    print('------')
    print(timeit.timeit("test2()", setup="from __main__ import test2"))

结果如下:

10.837343415012583
------
5.492854185053147

使用dis查看字节码

import dis
def list_c(nums):
    return [i*2 for i in nums]
print 'return [i*2 for i in nums]'
dis.dis(list_c)
print '--------------'
print '--------------'
print '--------------'
def append_list(nums):
    alist = []
    for i in nums:
        alist.append(i*2)
    return alist
dis.dis(append_list)

结果如下:

return [i*2 for i in nums]
  5           0 BUILD_LIST               0
              3 LOAD_FAST                0 (nums)
              6 GET_ITER            
        >>    7 FOR_ITER                16 (to 26)
             10 STORE_FAST               1 (i)
             13 LOAD_FAST                1 (i)
             16 LOAD_CONST               1 (2)
             19 BINARY_MULTIPLY     
             20 LIST_APPEND              2
             23 JUMP_ABSOLUTE            7
        >>   26 RETURN_VALUE        
--------------
--------------
--------------
 17           0 BUILD_LIST               0
              3 STORE_FAST               1 (alist)
 18           6 SETUP_LOOP              31 (to 40)
              9 LOAD_FAST                0 (nums)
             12 GET_ITER            
        >>   13 FOR_ITER                23 (to 39)
             16 STORE_FAST               2 (i)
 19          19 LOAD_FAST                1 (alist)
             22 LOAD_ATTR                0 (append)
             25 LOAD_FAST                2 (i)
             28 LOAD_CONST               1 (2)
             31 BINARY_MULTIPLY     
             32 CALL_FUNCTION            1
             35 POP_TOP             
             36 JUMP_ABSOLUTE           13
        >>   39 POP_BLOCK           
 20     >>   40 LOAD_FAST                1 (alist)
             43 RETURN_VALUE 

通过字节码,可以看出来,list comprehension的指令更少,不需要建立列表变量,同时,也就无需取变量,更不需要调用list的append函数(调用函数需要维护stack信息),也就比append要快。
以下是StackOverflow上某大神的回答:

A list comprehension is usually a tiny bit faster than the precisely equivalent for loop (that actually builds a list), most likely because it doesn’t have to look up the list and its append method on every iteration. However, a list comprehension still does a bytecode-level loop: dis.dis()
1 0 BUILD_LIST 0
3 LOAD_FAST 0 (.0)
6 FOR_ITER 12 (to 21)
9 STORE_FAST 1 (x)
12 LOAD_FAST 1 (x)
15 LIST_APPEND 2
18 JUMP_ABSOLUTE 6
21 RETURN_VALUE Using a list comprehension in place of a loop that doesn’t build a list, nonsensically accumulating a list of meaningless values and then throwing the list away, is often slower because of the overhead of creating and extending the list. List comprehensions aren’t magic that is inherently faster than a good old loop.

map和append的性能对比

def test():
    """Stupid test function"""
    oldlist = 'abcde'
    newlist = []
    for word in oldlist:
        newlist.append(word.upper())
    return newlist
def test2():
    oldlist = 'abcde'
    return map(str.upper, oldlist)
if __name__ == '__main__':
    import timeit
    print(timeit.timeit("test()", setup="from __main__ import test"))
    print('------')
    print(timeit.timeit("test2()", setup="from __main__ import test2"))

Python3测试结果如下:

1.077143672038801
------
0.3608201480237767

这里因为for循环是在解释器中执行的,而map则是编译好的C指令,所以比for循环性能更好。
参考:
https://wiki.python.org/moin/PythonSpeed/PerformanceTips#Loops
http://stackoverflow.com/questions/22108488/are-list-comprehensions-and-functional-functions-faster-than-for-loops
http://stackoverflow.com/questions/1247486/python-list-comprehension-vs-map

你可能感兴趣的:(Python list comprehension 和 append 性能对比)