Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/295.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Python3中,“list(generator expression)`的列表理解是语法糖吗?_Python_Python 3.x_List Comprehension_Python Internals_Generator Expression - Fatal编程技术网

在Python3中,“list(generator expression)`的列表理解是语法糖吗?

在Python3中,“list(generator expression)`的列表理解是语法糖吗?,python,python-3.x,list-comprehension,python-internals,generator-expression,Python,Python 3.x,List Comprehension,Python Internals,Generator Expression,在Python3中,列表理解是否只是馈送到list函数的生成器表达式的语法糖 e、 g.是以下代码: squares = [x**2 for x in range(1000)] 在后台实际转换为以下内容 squares = list(x**2 for x in range(1000)) 我知道输出是相同的,Python3修复了列表理解对周围名称空间产生的令人惊讶的副作用,但就CPython解释器在后台的工作而言,前者是否转换为后者,或者代码的执行方式是否有任何不同? 背景 我在评论部分找到

在Python3中,列表理解是否只是馈送到
list
函数的生成器表达式的语法糖

e、 g.是以下代码:

squares = [x**2 for x in range(1000)]
在后台实际转换为以下内容

squares = list(x**2 for x in range(1000))
我知道输出是相同的,Python3修复了列表理解对周围名称空间产生的令人惊讶的副作用,但就CPython解释器在后台的工作而言,前者是否转换为后者,或者代码的执行方式是否有任何不同?

背景 我在评论部分找到了这种等价性的说法,谷歌快速搜索也显示了同样的说法

报告中也提到了这一点,但措辞有些模糊:

还请注意,列表理解具有不同的语义:它们更接近list()构造函数中生成器表达式的语法糖,尤其是循环控制变量不再泄漏到周围的范围中


这两个表单都创建并调用匿名函数。但是,
list(…)
表单创建一个生成器函数,并将返回的生成器迭代器传递给
list
,而对于
[…]
表单,匿名函数直接使用
list\u APPEND
操作码构建列表

以下代码获取示例理解的匿名函数的反编译输出,并将其相应的genexp传递到-
列表

import dis

def f():
    [x for x in []]

def g():
    list(x for x in [])

dis.dis(f.__code__.co_consts[1])
dis.dis(g.__code__.co_consts[1])
理解的输出是

  4           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
genexp的输出是

  7           0 LOAD_FAST                0 (.0)
        >>    3 FOR_ITER                11 (to 17)
              6 STORE_FAST               1 (x)
              9 LOAD_FAST                1 (x)
             12 YIELD_VALUE
             13 POP_TOP
             14 JUMP_ABSOLUTE            3
        >>   17 LOAD_CONST               0 (None)
             20 RETURN_VALUE

两者的工作方式不同。列表理解版本利用了直接调用我们的特殊字节码。因此,它避免了对
list.append
的属性查找和Python级别的函数调用

>>> 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
时间比较:

>>> %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
由于属性查找速度较慢,普通循环稍慢。缓存它并重试

>>> %%timeit
out = [];append=out.append
for 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)
...
1
4
9
>[x**2代表1、2、3中的x]#Python 2
[1, 4, 9]
>>>[x**2表示1、2、3中的x]#Python 3
文件“”,第1行
[x**2表示1、2、3中的x]
^
SyntaxError:无效语法
>>>[x**2表示(1,2,3)中的x]#添加括号
[1, 4, 9]
>>>对于1、2、3中的x:#Python 3:对于普通循环,它仍然有效
打印(x**2)
...
1.
4.
9

事实上,你可以证明这两种方法有不同的结果,以证明它们本质上是不同的:

>>> list(next(iter([])) if x > 3 else x for x in range(10))
[0, 1, 2, 3]

>>> [next(iter([])) if x > 3 else x for x in range(10)]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <listcomp>
StopIteration
>>列表(下一个(iter([]),如果x>3,则为x,范围(10))内的x)
[0, 1, 2, 3]
>>>[下一步(iter([])如果x>3,则x为范围(10)内的x]
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
文件“”,第1行,在
停止迭代

理解中的表达式不被视为生成器,因为理解不处理
停止迭代
,而
列表
构造函数处理。

它们不一样,
列表()
将在括号中的内容完成执行后计算给它的内容,以前没有


python中的
[]
有点神奇,它告诉python将其中的内容包装成一个列表,更像是语言的类型提示。

我不确定这个假设是否正确。AFAIK列表理解是for循环的语法糖,而生成器表达式的语义大不相同——即它以迭代方式“生成”值。耸耸肩也许Python 3中的语义已经改变了:)NB:确切的措辞是“更接近
list()
constructor中的生成器表达式”--并不是说它们确实如此。@JamesMills谢谢,对,准确地说—“更接近语法糖”是让我有些困惑的一点。我是说,到底有多近?它确实不是语法上的甜点吗?如果有疑问,请使用
dis
模块进行检查。请注意,名称
列表
也需要显式查找,因为它可能已被隐藏。多亏了这一点,
dis
模块总是很有见地,但有时也有点神秘-在您的genexp示例中,如果我读得正确的话,列表创建看起来会被完全省略,但我不知道为什么…@zehnpaard:这不是匿名函数的一部分;
列表
调用处理这个问题。啊,这很公平,而且
g.\uuuu code\uuu.co\u consts[1]
特别指向匿名函数?@zehnpaard:它指向用于构造匿名函数的代码对象。感谢您的详细响应!有趣的是,尽管底层字节码(和C代码)非常不同,但timeit如何显示列表理解和被抛出到
列表中的生成器表达式之间的可忽略差异@zehnpaard引用Guido的话:在你开始担心Python 3中的列表理解速度变慢之前:多亏了Python 3中的大量实现工作,总体上加快了速度,Python 3中的列表理解和生成器表达式实际上都比Python 2中的快!(并且两者之间不再存在速度差异。)如何直接从python shell使用百分号运行timeit?@ZaarHai这是ipython shell,使用
--classic
参数运行。@zehnpaard差异(不再)可以忽略-第一个版本大约快30%,另请参见注释,在Python3.7/3.8中,最上面的一个引发了
RuntimeError:generator引发的StopIteration
请参见
>>> list(next(iter([])) if x > 3 else x for x in range(10))
[0, 1, 2, 3]

>>> [next(iter([])) if x > 3 else x for x in range(10)]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <listcomp>
StopIteration