提问人:Amir reza Riahi 提问时间:11/16/2023 最后编辑:Abdul Niyas P MAmir reza Riahi 更新时间:11/19/2023 访问量:1864
为什么列表推导式会在内部创建一个函数?
Why list comprehensions create a function internally?
问:
这是 python 3.10 中列表推导式的反汇编:
Python 3.10.12 (main, Jun 11 2023, 05:26:28) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import dis
>>>
>>> dis.dis("[True for _ in ()]")
1 0 LOAD_CONST 0 (<code object <listcomp> at 0x7fea68e0dc60, file "<dis>", line 1>)
2 LOAD_CONST 1 ('<listcomp>')
4 MAKE_FUNCTION 0
6 LOAD_CONST 2 (())
8 GET_ITER
10 CALL_FUNCTION 1
12 RETURN_VALUE
Disassembly of <code object <listcomp> at 0x7fea68e0dc60, file "<dis>", line 1>:
1 0 BUILD_LIST 0
2 LOAD_FAST 0 (.0)
>> 4 FOR_ITER 4 (to 14)
6 STORE_FAST 1 (_)
8 LOAD_CONST 0 (True)
10 LIST_APPEND 2
12 JUMP_ABSOLUTE 2 (to 4)
>> 14 RETURN_VALUE
据我了解,它创建了一个代码对象,该对象执行实际迭代并返回结果列表,并立即调用它。
我无法理解需要创建一个单独的函数来执行这项工作。这是一种优化技巧吗?listcomp
答:
创建函数的主要逻辑是隔离推导式的迭代变量peps.python.org。
通过创建函数:
理解迭代变量保持隔离,不会覆盖 外部作用域中同名的变量,也不可见 领悟后
但它在运行时效率低下。正因为如此,python-3.12 实现了一个名为 comprehension inlining (PEP 709) 的优化 peps.python.org 这将不再创建单独的代码对象peps.python.org。
字典、列表和集合推导式现在是内联的,而不是 为每次执行 理解。这将理解的执行速度提高多达 两次。有关详细信息,请参阅 PEP 709。
以下是使用 python-3.12 反汇编的相同代码的输出:
>>> import dis
>>>
>>> dis.dis("[True for _ in ()]")
0 0 RESUME 0
1 2 LOAD_CONST 0 (())
4 GET_ITER
6 LOAD_FAST_AND_CLEAR 0 (_)
8 SWAP 2
10 BUILD_LIST 0
12 SWAP 2
>> 14 FOR_ITER 4 (to 26)
18 STORE_FAST 0 (_)
20 LOAD_CONST 1 (True)
22 LIST_APPEND 2
24 JUMP_BACKWARD 6 (to 14)
>> 26 END_FOR
28 SWAP 2
30 STORE_FAST 0 (_)
32 RETURN_VALUE
>> 34 SWAP 2
36 POP_TOP
38 SWAP 2
40 STORE_FAST 0 (_)
42 RERAISE 0
ExceptionTable:
10 to 26 -> 34 [2]
如您所见,不再有操作码。相反,python-3.12 使用LOAD_FAST_AND_CLEAR
docs.python.org(at offset ) 和 (at offset ) 操作码来为迭代变量提供隔离。MAKE_FUNCTION
6
STORE_FAST
30
引自 PEP 709 的规范部分peps.python.org:
迭代变量的隔离是通过组合实现的 在偏移处的新操作码,它保存了任何 在运行推导器之前,堆栈的外部值,以及 ,在运行后恢复(如果有)的外部值 理解。
x
LOAD_FAST_AND_CLEAR
6
x
30
STORE_FAST
x
以下是基准测试结果peps.python.org(使用 MacOS M2 测量):
$ python3.10 -m pyperf timeit -s 'l = [1]' '[x for x in l]'
Mean +- std dev: 108 ns +- 3 ns
$ python3.12 -m pyperf timeit -s 'l = [1]' '[x for x in l]'
Mean +- std dev: 60.9 ns +- 0.3 ns
评论
规范
部分,了解 PEP 709 如何为迭代变量提供隔离。另请注意,此 PEP 引入了一些可见的行为更改,此处列出了这些更改
LOAD_FAST_AND_CLEAR
上一个:项目声音
评论