当前位置: 首页 > 面试题库 >

列表理解是Python 3中`list(generator expression)`的语法糖吗?

方飞白
2023-03-14
问题内容

在Python 3中,列表理解是否只是馈给list函数的生成器表达式的语法糖?

例如下面的代码:

squares = [x**2 for x in range(1000)]

实际在后台转换为以下内容?

squares = list(x**2 for x in range(1000))

我知道输出是相同的,Python
3修复了列出理解的周围名称空间的令人惊讶的副作用,但是就CPython解释器的作用而言,是前者转换为后者,还是有任何区别?代码如何执行?

背景

我在此问题的评论部分找到了这种对等的主张,而谷歌的快速搜索显示此处也提出了同样的主张。

“ Python 3.0的新增功能”文档中也提到了这一点,但措辞有些含糊:

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


问题答案:

两者的工作方式不同。该列表理解的版本需要特殊的字节码的优势,LIST_APPEND这就要求PyList_Append直接给我们。因此,它避免了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

另一方面,list()版本仅将生成器对象传递给list的__init__方法,然后在extend内部调用其方法。由于对象不是列表或元组,因此CPython首先获取其迭代器,然后简单地将项目添加到列表中,直到迭代器用尽:

>>> 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

由于属性查找缓慢,因此正常循环会稍慢。缓存它,然后再次计时。

>>> %%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


 类似资料:
  • 在CoffeeScript中,我们还可以在数组中存储一组对象。 list推导用于将对象数组映射到另一个数组。 语法 (Syntax) 假设我们在CoffeeScript中有一个对象数组,如[{key1: "value", key2: value}, {key1: "value", key2: value}]那么你可以使用list [{key1: "value", key2: value}, {ke

  • Python3 列表 描述 list() 方法用于将元组转换为列表。 注:元组与列表是非常类似的,区别在于元组的元素值不能修改,元组是放在括号中,列表是放于方括号中。 语法 list()方法语法: list( seq ) 参数 list -- 要转换为列表的元组。 返回值 返回列表。 实例 以下实例展示了 list()函数的使用方法: #!/usr/bin/python3 aTuple =

  • 主要内容:初始化列表,在列表中插入元素,从列表中删除元素,遍历列表——访问列表的每一个元素列表是一种非连续的存储容器,由多个节点组成,节点通过一些变量记录彼此之间的关系,列表有多种实现方法,如单链表、双链表等。 列表的原理可以这样理解:假设 A、B、C 三个人都有电话号码,如果 A 把号码告诉给 B,B 把号码告诉给 C,这个过程就建立了一个单链表结构,如下图所示。 图:三人单向通知电话号码形成单链表结构 如果在这个基础上,再从 C 开始将自己的号码告诉给自己所知道号码的主人,这样就形

  • 本文向大家介绍Java8语法糖之Lambda表达式的深入讲解,包括了Java8语法糖之Lambda表达式的深入讲解的使用技巧和注意事项,需要的朋友参考一下 一、Lambda表达式简介 Lambda表达式,是Java8的一个新特性,也是Java8中最值得学习的新特性之一。(另一个新特性是流式编程。) Lambda表达式,从本质上讲是一个匿名方法。可以使用这个匿名方法,实现接口中的方法。 功能:通常使

  • 本文向大家介绍C# using语法糖图文详解,包括了C# using语法糖图文详解的使用技巧和注意事项,需要的朋友参考一下 前言 什么是语法糖? (语法糖就是像糖一样的语法…) 语法糖(Syntactic sugar),又名糖衣语法,最早是由英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的。 通俗点来讲就是简化后的语法,但是其效果和原先语法是一样的,只是更方便我们程序员使用

  • 本文向大家介绍详解es6超好用的语法糖Decorator,包括了详解es6超好用的语法糖Decorator的使用技巧和注意事项,需要的朋友参考一下 Decorator(修饰器/装饰器)是es6提出的语法糖,用于修改类的行为。不过目前主流浏览器都没有很好的支持,我们需要用babel来转换为浏览器能识别的语言。在这篇文章中将介绍decorator的基础用法和一些应用实例。 1.修饰类 (1) 基础用法