两者的工作方式不同。该列表理解的版本需要特殊的字节码的优势,
LIST_APPEND这就要求
PyList_Append直接给我们。因此,它避免了
list.append在Python级别进行属性查找和函数调用。
>>> def func_lc(): [x**2 for x in y]...>>> dis.dis(func_lc) 20 LOAD_ConST 1 (<pre object <listcomp> at 0x10d3c6780, file "<ipython-input-42-ead395105775>", line 2>) 3 LOAD_ConST 2 ('func_lc.<locals>.<listcomp>') 6 MAKE_FUNCTION 0 9 LOAD_GLOBAL 0 (y) 12 GET_ITER 13 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 16 POP_TOP 17 LOAD_ConST 0 (None) 20 RETURN_VALUE>>> lc_object = list(dis.get_instructions(func_lc))[0].argval>>> lc_object<pre object <listcomp> at 0x10d3c6780, file "<ipython-input-42-ead395105775>", line 2>>>> dis.dis(lc_object) 20 BUILD_LIST 0 3 LOAD_FAST 0 (.0) >> 6 FOR_ITER 16 (to 25) 9 STORE_FAST 1 (x) 12 LOAD_FAST 1 (x) 15 LOAD_ConST 0 (2) 18 BINARY_POWER 19 LIST_APPEND 2 22 JUMP_ABSOLUTE 6 >> 25 RETURN_VALUE另一方面,
list()版本仅将生成器对象传递给list的
__init__方法,然后在
extend内部调用其方法。由于对象不是列表或元组,因此CPython首先获取其迭代器,然后简单地将项目添加到列表中,直到迭代器用尽:
>>> def func_ge(): list(x**2 for x in y)...>>> dis.dis(func_ge) 20 LOAD_GLOBAL 0 (list) 3 LOAD_ConST 1 (<pre object <genexpr> at 0x10cde6ae0, file "<ipython-input-41-f9a53483f10a>", line 2>) 6 LOAD_ConST 2 ('func_ge.<locals>.<genexpr>') 9 MAKE_FUNCTION 0 12 LOAD_GLOBAL 1 (y) 15 GET_ITER 16 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 19 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 22 POP_TOP 23 LOAD_ConST 0 (None) 26 RETURN_VALUE>>> ge_object = list(dis.get_instructions(func_ge))[1].argval>>> ge_object<pre object <genexpr> at 0x10cde6ae0, file "<ipython-input-41-f9a53483f10a>", line 2>>>> dis.dis(ge_object) 20 LOAD_FAST 0 (.0) >> 3 FOR_ITER 15 (to 21) 6 STORE_FAST 1 (x) 9 LOAD_FAST 1 (x) 12 LOAD_ConST 0 (2) 15 BINARY_POWER 16 YIELD_VALUE 17 POP_TOP 18 JUMP_ABSOLUTE 3 >> 21 LOAD_ConST 1 (None) 24 RETURN_VALUE>>>时序比较:
>>> %timeit [x**2 for x in range(10**6)]1 loops, best of 3: 453 ms per loop>>> %timeit list(x**2 for x in range(10**6))1 loops, best of 3: 478 ms per loop>>> %%timeitout = []for x in range(10**6): out.append(x**2)...1 loops, best of 3: 510 ms per loop
由于属性查找缓慢,因此正常循环会稍慢。缓存它,然后再次计时。
>>> %%timeitout = [];append=out.appendfor x in range(10**6): append(x**2)...1 loops, best of 3: 467 ms per loop
除了列表理解不再泄漏变量的事实之外,另一个区别是类似的东西不再有效:
>>> [x**2 for x in 1, 2, 3] # Python 2[1, 4, 9]>>> [x**2 for x in 1, 2, 3] # Python 3 File "<ipython-input-69-bea9540dd1d6>", line 1 [x**2 for x in 1, 2, 3] ^SyntaxError: invalid syntax>>> [x**2 for x in (1, 2, 3)] # Add parenthesis[1, 4, 9]>>> for x in 1, 2, 3: # Python 3: For normal loops it still works print(x**2)...149



