问题描述
在Python 3中,列表理解是否只是馈给list
函数的生成器表达式的语法糖?
In Python 3, is a list comprehension simply syntactic sugar for a generator expression fed into the list
function?
例如是以下代码:
squares = [x**2 for x in range(1000)]
实际上是在后台转换为以下内容吗?
actually converted in the background into the following?
squares = list(x**2 for x in range(1000))
我知道输出是相同的,并且Python 3修复了列出理解的周围名称空间的令人惊讶的副作用,但是就CPython解释器的作用而言,是前者转换为后者,还是代码的执行方式有什么不同?
I know the output is identical, and Python 3 fixes the surprising side-effects to surrounding namespaces that list comprehensions had, but in terms of what the CPython interpreter does under the hood, is the former converted to the latter, or are there any difference in how the code gets executed?
我在此问题的评论部分中找到了这种对等的主张,并且谷歌快速搜索显示在此处.
I found this claim of equivalence in the comments section to this question, and a quick google search showed the same claim being made here.
在 Python 3.0的新增功能中也提到了这一点文档,但措辞有些含糊:
推荐答案
两者的工作方式有所不同,列表理解版本利用特殊字节码 LIST_APPEND
,它调用 PyList_Append 直接给我们.因此,它避免了对list.append
的属性查找和在Python级别的函数调用.
Both work differently, the list comprehension version takes the advantage of special bytecode LIST_APPEND
which calls PyList_Append directly for us. Hence it avoids attribute lookup to list.append
and function call at Python level.
>>> def func_lc():
[x**2 for x in y]
...
>>> dis.dis(func_lc)
2 0 LOAD_CONST 1 (<code 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
<code object <listcomp> at 0x10d3c6780, file "<ipython-input-42-ead395105775>", line 2>
>>> dis.dis(lc_object)
2 0 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()
版本只是将生成器对象传递到列表的 __init__
方法,然后调用其 extend
内部方法.由于该对象不是列表或元组,因此CPython然后获得其迭代器首先,然后只需将项目添加到列表中,直到迭代器已精疲力竭:
On the other hand the list()
version simply passes the generator object to list's __init__
method which then calls its extend
method internally. As the object is not a list or tuple CPython then gets its iterator first and then simply adds the items to the list until the iterator is exhausted:
>>> def func_ge():
list(x**2 for x in y)
...
>>> dis.dis(func_ge)
2 0 LOAD_GLOBAL 0 (list)
3 LOAD_CONST 1 (<code 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
<code object <genexpr> at 0x10cde6ae0, file "<ipython-input-41-f9a53483f10a>", line 2>
>>> dis.dis(ge_object)
2 0 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
>>> %%timeit
out = []
for x in range(10**6):
out.append(x**2)
...
1 loops, best of 3: 510 ms per loop
由于缓慢的属性查找,普通循环会稍微慢一些.对其进行缓存,然后再次计时.
Normal loops are slightly slow due to slow attribute lookup. Cache it and time again.
>>> %%timeit
out = [];append=out.append
for x in range(10**6):
append(x**2)
...
1 loops, best of 3: 467 ms per loop
除了列表理解不再泄漏变量的事实之外,另一个区别是类似的东西不再有效:
Apart from the fact that list comprehension don't leak the variables anymore one more difference is that something like this is not valid anymore:
>>> [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)
...
1
4
9
这篇关于列表理解是Python 3中`list(generator expression)`的语法糖吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!