本文档描述了 CPython 如何将 Python 源代码转换为解释器可执行的字节码的过程。编译管道是一个多阶段过程,它分析源代码、创建数据结构来表示代码语义,并生成优化的字节码指令。
有关字节码如何执行的信息,请参阅字节码解释器与优化。
Python 编译管道包含几个不同的阶段,每个阶段在将源代码转换为可执行字节码的过程中都有其自身的职责。
编译过程的主要入口点是 _PyAST_Compile(),它以正确的顺序协调这些步骤。每个阶段都建立在前一个阶段的输出之上,逐步将源代码转换为最终的字节码表示。
编译过程的第一步是将 Python 源代码解析成抽象语法树(AST)。AST 是一种分层树结构,表示代码的语法结构。树中的每个节点对应一个语言构造(语句、表达式等)。
虽然解析器是编译管道的一个独立组件,但理解 AST 是编译过程的输入非常重要。AST 在传递到下一阶段之前会经过优化。
符号表是编译过程中的关键数据结构。它将标识符(变量名)映射到程序中的作用域和属性。
符号表构建器遍历 AST 并分析变量使用情况
符号表由 PySTEntryObject 结构组成,这些结构按层次结构组织,以匹配代码的词法结构。
来源:Python/symtable.c387-489 Python/compile.c140-141
代码生成阶段根据符号表将 AST 转换为指令序列。此阶段遍历 AST 并为每个节点发出适当的字节码指令。
代码生成器以不同方式处理各种 Python 构造
输出是表示原始代码逻辑的指令序列,但尚未优化。
来源:Python/compile.c811-853 Python/codegen.c1-14
控制流图(CFG)将指令序列表示为基本块的图,其中每个块是一系列按顺序执行的指令(在入口和出口处没有跳转)。
CFG 创建包括
CFG 表示有助于对程序中的控制流进行分析和优化。
来源:Python/flowgraph.c327-457 Python/compile.c10-11
一旦构建了控制流图,编译器就会应用各种优化来提高代码效率。
1+2 替换为 3)编译过程的最后阶段是汇编,它将优化的控制流图转换回平坦的字节码指令序列,并生成 PyCodeObject 所需的元数据。
汇编过程包括
结果是一个 PyCodeObject,其中包含字节码解释器执行代码所需的所有信息。
来源:Python/assemble.c32-366 Python/compile.c12-13
让我们深入了解每个编译阶段涉及的具体函数
| 阶段 | 主函数 | 输入 | 输出 |
|---|---|---|---|
| 符号表生成 | _PySymtable_Build() | AST | 符号表 |
| 代码生成 | compiler_codegen() | AST、符号表 | 指令序列 |
| CFG 创建 | _PyCfg_FromInstructionSequence() | 指令序列 | 控制流图 |
| 优化 | _PyCfg_OptimizeCodeUnit() | 控制流图 | 优化后的 CFG |
| 汇编 | _PyAssemble_MakeCodeObject() | 优化后的 CFG | PyCodeObject |
符号表生成阶段(在 symtable.c 中实现)会分析整个代码单元的变量使用情况。
此阶段确定
来源:Python/symtable.c412-489 Python/symtable.c477-482
代码生成阶段(在 codegen.c 中实现)会遍历 AST 并生成指令。
AST 中的不同节点类型由专门的函数处理,这些函数会发出适当的指令。
来源:Python/codegen.c15-30 Python/compile.c838-850
CFG 构建和优化阶段(在 flowgraph.c 中实现)将线性指令序列转换为图结构。
优化侧重于改进控制流和减少指令数量,而不改变程序语义。
来源:Python/flowgraph.c584-990 Python/compile.c10-11
compile.c 中的 _PyAST_Compile() 函数协调了整个编译过程。
该函数将所有编译阶段联系在一起,从初始设置到最终的 PyCodeObject 创建。
CPython 的编译管道是一个复杂的、多阶段的过程,它将 Python 源代码转换为高效的字节码。每个阶段都建立在前一个阶段之上,逐渐完善代码的表示,直到达到最终的可执行形式。
理解这个管道对于想要以下的用户至关重要:
编译管道旨在平衡编译速度和优化质量,生成可由解释器高效执行的字节码。