25. 字节码生成

字节码生成是 CPython 将结构化语法转换为可执行虚拟机指令的阶段。

解析器构建 AST。

符号表决定范围行为。

然后编译器发出实现 Python 语义的字节码指令。

对于这个来源:python id="bjlwm8" def add(a, b): return a + b CPython 生成的字节码形状如下:text id="plhxq4" LOAD_FAST a LOAD_FAST b BINARY_OP + RETURN_VALUE 确切的指令名称和格式因 Python 版本而异,但模型保持稳定:```text id="ecx81v" bytecode is a low-level instruction stream executed by the CPython virtual machine


字节码生成发生在 AST 构建和范围分析之后。```text id="5v2o9v"
source
    
tokenization
    
parsing
    
AST
    
symbol table
    
bytecode generation
    
code object
    
evaluation loop
```编译器遍历 AST 并发出指令和元数据。

输出成为代码对象的一部分。

## 25.2 字节码代表什么

字节码是 CPython 的虚拟指令集。

它不是机器代码。

它不是源代码。

它是为 CPython 解释器设计的中间执行语言。

示例来源:```python id="mwhlwe"
x = 1 + 2
```可能的字节码:```text id="cv4c36"
LOAD_CONST 3
STORE_NAME x
LOAD_CONST None
RETURN_VALUE
```解释器随后将这些指令一一执行。

编译器的工作是:```text id="wlk7tq"
preserve Python semantics
emit correct stack behavior
emit correct control flow
emit correct scope access
record source metadata
```## 25.3 字节码是基于堆栈的

CPython 字节码使用堆栈机。

大多数指令从计算堆栈中压入或弹出值。

例子:```python id="qggm8k"
a + b
```汇编:```text id="jlwm0j"
LOAD_FAST a
LOAD_FAST b
BINARY_OP +
```堆栈演变:```text id="4tq4zg"
LOAD_FAST a
    stack: a

LOAD_FAST b
    stack: a, b

BINARY_OP +
    pop a and b
    push result
    stack: result
```编译器必须保持堆栈效果一致。

每个字节码路径必须维护有效的堆栈状态。

## 25.4 表达式编译

表达式产生值。

例子:```python id="nl8bg0"
x + y * z
```AST 保留优先级:```text id="0u6c93"
x + (y * z)
```Bytecode generation follows that structure.

概念字节码:```text id="jlwm0m"
LOAD_FAST x
LOAD_FAST y
LOAD_FAST z
BINARY_OP *
BINARY_OP +
```堆栈演变:```text id="f2zv3g"
x
x, y
x, y, z
x, temp
result
```编译器递归地编译子表达式。

## 25.5 语句编译

语句通常会发出副作用指令。

例子:```python id="rj0nwu"
x = value
```汇编:```text id="zyulq0"
compile expression value
store into target x
```可能的字节码:```text id="jlwm0n"
LOAD_FAST value
STORE_FAST x
```例子:```python id="9hq2o6"
return x
```汇编:```text id="jlwm0o"
LOAD_FAST x
RETURN_VALUE
```编译器区分:```text id="n0c2jr"
expressions producing values
statements performing actions
```## 25.6 加载常量

常量加载自`co_consts`

例子:```python id="s7hy3e"
x = 123
```字节码:```text id="jlwm0p"
LOAD_CONST 123
STORE_NAME x
```编译器将常量插入到`co_consts`并发出索引引用。

从概念上讲:```text id="j49sul"
co_consts:
    0: 123

instruction:
    LOAD_CONST 0
```解释器在执行期间解析索引。

## 25.7 加载局部变量

快速局部变量使用索引槽。

例子:```python id="bhz0x7"
def f(a, b):
    return a + b
```字节码:```text id="jlwm0q"
LOAD_FAST a
LOAD_FAST b
BINARY_OP +
RETURN_VALUE
```编译器使用`LOAD_FAST`因为符号分析分类`a``b`作为局部变量。

快速的本地人避免字典查找。

## 25.8 加载全局变量

全局查找和内置查找使用不同的指令。

例子:```python id="y3afqz"
x = 10

def f():
    return x
```里面`f`:

```text id="jlwm0r"
LOAD_GLOBAL x
RETURN_VALUE
```口译员检查:```text id="l1ajyz"
function globals
then builtins
```编译器选择`LOAD_GLOBAL`基于符号表信息。

## 25.9 加载闭包变量

闭包变量使用解引用操作。

例子:```python id="cv9ngd"
def outer():
    x = 1

    def inner():
        return x
```里面`inner`:

```text id="jlwm0s"
LOAD_DEREF x
RETURN_VALUE
```编译器发出解引用字节码,因为`x`是从封闭范围捕获的自由变量。

闭包字节码访问单元对象而不是普通的本地槽。

## 25.10 分配目标

赋值目标的编译方式与普通表达式不同。

例子:```python id="mav3fq"
x = 1
```字节码:```text id="jlwm0t"
LOAD_CONST 1
STORE_NAME x
```但属性赋值:```python id="y7g47q"
obj.value = 1
```字节码形状:```text id="jlwm0u"
LOAD_FAST obj
LOAD_CONST 1
STORE_ATTR value
```下标赋值:```python id="oxtk8t"
items[i] = value
```字节码形状:```text id="jlwm0v"
LOAD_FAST items
LOAD_FAST i
LOAD_FAST value
STORE_SUBSCR
```目标编译取决于 AST 上下文。

## 25.11 删除

删除使用专用指令。

例子:```python id="d8j5gc"
del x
```可能的字节码:```text id="jlwm0w"
DELETE_FAST x
```例子:```python id="b4d5gn"
del obj.attr
```可能的字节码:```text id="jlwm0x"
LOAD_FAST obj
DELETE_ATTR attr
```删除不是赋值给`None`。它根据目标类型删除绑定或对象条目。

## 25.12 函数调用

函数调用生成多条指令。

例子:```python id="a3j0ut"
f(x, y)
```概念字节码:```text id="jlwm0y"
LOAD_NAME f
LOAD_FAST x
LOAD_FAST y
CALL 2
POP_TOP
```编译器必须:```text id="e8qjkl"
compile callable expression
compile positional arguments
compile keyword arguments
emit call instruction
handle stack layout
```方法调用可以使用专门的字节码形式。

例子:```python id="s4m8cq"
obj.run()
```可能的形状:```text id="jlwm0z"
LOAD_FAST obj
LOAD_METHOD run
CALL 0
```现代 CPython 版本包含额外的专门化和围绕调用的内联缓存行为。

## 25.13 二元运算

算术和二元运算发出运算指令。

例子:```python id="gt4c4x"
a + b
```字节码:```text id="jlwm10"
LOAD_FAST a
LOAD_FAST b
BINARY_OP +
```其他例子:

|表达 | Operation       |
| ---------- | ---------------- |
|`a - b`|减法|
|`a * b`|乘法|
|`a / b`|部门 |
|`a // b`|地板事业部|
|`a % b`||
|`a ** b`|电源 |
|`a @ b`|矩阵乘法 |
|`a << b`|左移|
|`a & b`|按位与|

编译器发出操作指令。运行时类型调度稍后发生。

例子:```python id="x8vwdk"
1 + 2
"a" + "b"
```两者的编译方式类似,但运行时对象行为不同。

## 25.14 比较

比较发出比较操作。

例子:```python id="e98twa"
a < b
```可能的字节码:```text id="jlwm11"
LOAD_FAST a
LOAD_FAST b
COMPARE_OP <
```链式比较需要更复杂的控制流程。

例子:```python id="pxo93r"
a < b < c
```这个必须评价`b`一次。

概念编译:```text id="jlwm12"
LOAD_FAST a
LOAD_FAST b
COMPARE_OP <
conditional jump if false
LOAD_FAST b
LOAD_FAST c
COMPARE_OP <
```编译器保留了 Python 的链式比较语义。

## 25.15 布尔运算

布尔运算短路。

例子:```python id="dd8v9m"
a and b
```编译模式:```text id="jlwm13"
evaluate a
jump if false
evaluate b

b仅在需要时执行。

相似地:python id="a3pgh7" a or b 评估b仅当a是假的。

短路行为是通过跳转实现的,而不是普通的函数调用。

25.16 条件表达式

示例:python id="r74d92" x if cond else y 编译模式:```text id="jlwm14" evaluate cond jump to else branch if false evaluate x jump to end evaluate y


## 25.17`if`声明

示例:```python id="q70zfx"
if cond:
    a()
else:
    b()
```编译模式:```text id="jlwm15"
compile condition
jump to else if false
compile a()
jump to end
compile b()
end
```可能的字节码形状:```text id="jlwm16"
LOAD_NAME cond
POP_JUMP_IF_FALSE else_label

LOAD_NAME a
CALL
POP_TOP
JUMP_FORWARD end_label

else_label:
LOAD_NAME b
CALL
POP_TOP

end_label:
```编译器在最终汇编之前在内部管理标签和跳转目标。

## 25.18`while`循环

示例:```python id="0wdg4v"
while cond:
    work()
```编译模式:```text id="jlwm17"
loop_start:
    evaluate cond
    jump to end if false
    compile body
    jump to loop_start
loop_end:
```循环需要块堆栈跟踪:```text id="jlwm18"
break
continue
exception cleanup
```## 25.19`for`循环

示例:```python id="uk10dk"
for item in items:
    work(item)
```编译模式:```text id="jlwm19"
load iterable
get iterator

loop_start:
    get next item
    jump to end on StopIteration
    store item
    compile body
    jump to loop_start

loop_end:
```编译器发出迭代器协议字节码。

一条蟒蛇`for`循环是迭代器驱动的,而不是索引驱动的。

## 25.20`break`和`continue`例子:```python id="h5f6ov"
while True:
    if stop:
        break

    continue

break跳转到循环退出。continue跳转到循环继续点。

编译器维护循环上下文结构,以便嵌套循环正常运行。

例子:python id="r7bz0d" for x in xs: for y in ys: break 内部break仅退出内循环。

25.21 异常处理

异常处理需要结构化的控制流元数据。

例子:python id="12cm6v" try: risky() except ValueError: recover() finally: cleanup() 编译职责:```text id="jlwm1a" protected instruction ranges exception handler targets finally cleanup reraising behavior stack restoration


编译器记录:```text id="jlwm1b"
instruction range
handler entry
handler type
stack depth information
```当异常发生时,此元数据使解释器能够正确跳转到处理程序。

## 25.22`with`声明

示例:```python id="wq6o7z"
with open(path) as f:
    data = f.read()
```编译模式:```text id="jlwm1c"
evaluate context manager
call __enter__
store result
execute body
ensure __exit__ runs
handle exceptions correctly
```编译器发出清理逻辑,确保`__exit__`即使发生异常也会执行。`with`编译与异常处理机制紧密相连。

## 25.23 函数定义

函数定义分两个阶段编译。

例子:```python id="8k2d07"
def f(x):
    return x + 1
```第一阶段:```text id="jlwm1d"
compile function body into nested code object
```第二阶段:```text id="jlwm1e"
emit runtime instructions creating function object
```概念字节码:```text id="jlwm1f"
LOAD_CONST <code object f>
MAKE_FUNCTION
STORE_NAME f
```主体本身成为嵌套代码对象内的字节码。

## 25.24 关闭

示例:```python id="b4pshk"
def outer():
    x = 1

    def inner():
        return x
```编译职责:```text id="jlwm1g"
create closure cell for x
compile inner with free variable access
pass closure tuple during function creation
```内部可能的字节码形状`outer`:

```text id="jlwm1h"
MAKE_CELL x
LOAD_CONST 1
STORE_DEREF x

LOAD_CLOSURE x
BUILD_TUPLE 1
LOAD_CONST <code object inner>
MAKE_FUNCTION closure
```里面`inner`:

```text id="jlwm1i"
LOAD_DEREF x
RETURN_VALUE
```## 25.25 类定义

示例:```python id="1br4s3"
class C:
    x = 1
```类主体成为嵌套代码对象。

编译模式:```text id="jlwm1j"
compile class body code object
emit runtime class construction logic
bind resulting class object
```类体像具有自己的命名空间的迷你模块一样执行。

方法成为类主体代码对象内的嵌套函数定义。

## 25.26 推导式

推导式编译成嵌套作用域。

例子:```python id="9w8n9x"
[x * x for x in xs]
```编译职责:```text id="jlwm1k"
create nested comprehension code object
iterate input iterable
bind local iteration variable
append results
return constructed container
```理解变量不会泄漏到外部作用域,因为编译器创建单独的执行作用域机制。

## 25.27 发电机

生成器函数使用悬挂点。

例子:```python id="i8gtwx"
def gen():
    yield 1
    yield 2
```编译职责:```text id="jlwm1l"
mark code object as generator
emit yield instructions
preserve resumable execution state
```可能的字节码形状:```text id="jlwm1m"
LOAD_CONST 1
YIELD_VALUE

LOAD_CONST 2
YIELD_VALUE

LOAD_CONST None
RETURN_VALUE
```框架必须在整个悬挂过程中保持状态。

## 25.28 协程和`await`例子:```python id="oqf8so"
async def fetch():
    return await client.get()
```编译职责:```text id="jlwm1n"
mark coroutine flags
emit await handling
preserve suspension semantics
```编译器为协程调度行为生成字节码,而不是普通的同步调用。

## 25.29 进口

示例:```python id="3k7d9u"
import os
```编译模式:```text id="jlwm1o"
IMPORT_NAME os
STORE_NAME os
```例子:```python id="38ef9k"
from math import sin
```编译模式:```text id="jlwm1p"
IMPORT_NAME math
IMPORT_FROM sin
STORE_NAME sin
```编译器发出导入操作。实际的模块加载发生在运行时。

## 25.30 断言

示例:```python id="g2m5qn"
assert x > 0
```编译模式:```text id="jlwm1q"
evaluate condition
jump if true
raise AssertionError
```在优化模式下(`python -O`)assert 语句可以完全省略。

这是编译器级别的转换。

## 25.31 源位置和行表

编译器记录字节码和源位置之间的映射。

这些映射支持:```text id="jlwm1r"
tracebacks
debuggers
profilers
coverage tools
stepping
error reporting
```每个指令范围可以对应于:```text id="jlwm1s"
line number
column offset
end line
end column
```代码对象存储压缩的映射表。

## 25.32 堆栈大小计算

编译器计算最大堆栈深度。

例子:```python id="7qdzg3"
a + b * c
```可能的堆栈演变:```text id="jlwm1t"
a
a, b
a, b, c
a, temp
result
```最大深度:3

这变成了`co_stacksize`

帧根据该值分配足够的堆栈空间。

## 25.33 基本块

在内部,编译器通常将指令分组为基本块。

基本块是一系列指令,其中:```text id="jlwm1u"
single entry
single exit
no internal jumps
```例子:```python id="w2g2q7"
if cond:
    a()
b()
```可能的块结构:```text id="jlwm1v"
block 1:
    evaluate cond
    conditional jump

block 2:
    a()
    jump

block 3:
    b()
```基本块简化了控制流分析和跳转解析。

## 25.34 跳转分辨率

编译器最初发出符号标签。

稍后的汇编会解决实际的指令偏移量。

概念过程:```text id="jlwm1w"
emit instructions
emit labels
calculate instruction offsets
replace labels with offsets
insert extended arguments if needed
recalculate offsets if sizes changed
```跳转分辨率是编译是多阶段的原因之一。

## 25.35 内联缓存和专业化支持

现代 CPython 字节码支持自适应专业化。

编译器可以发出与指令相关联的高速缓存条目。

受益于专业化的示例操作:```text id="jlwm1x"
attribute access
global lookup
binary operations
calls
method dispatch
```初始字节码是通用的。

运行时专业化稍后可能会用优化的快速路径取代行为。

编译器准备允许这种适应的指令布局。

## 25.36 字节码检查

使用`dis`检查生成的字节码。

例子:```python id="jlwm1y"
import dis

def f(a, b):
    return a + b

dis.dis(f)
```有用的检查功能包括:```text id="jlwm1z"
dis.dis
dis.Bytecode
dis.get_instructions
```字节码检查对于以下方面至关重要:```text id="jlwm20"
compiler debugging
performance analysis
tooling
education
reverse engineering Python behavior
```## 25.37 版本敏感性

CPython 版本之间的字节码会发生变化。

变化可能包括:```text id="jlwm21"
new opcodes
removed opcodes
opcode renaming
instruction format changes
specialization changes
exception handling changes
line table changes
```工具应避免依赖于跨版本的确切字节码布局,除非存在特定于版本的支持。

尽可能使用公共 API,而不是手动解析原始字节码。

## 25.38 重要的 CPython 源代码区域

重要文件包括:```text id="jlwm22"
Python/compile.c
Python/flowgraph.c
Python/assemble.c
Python/bytecodes.c
Include/opcode_ids.h
Lib/dis.py
Lib/opcode.py
```概念角色:

|面积 |角色 |
| ------------- | -------------------------------------- |
|`compile.c`| AST 遍历和指令发射 |
|`flowgraph.c`|控制流图处理 |
|`assemble.c`|字节码汇编和跳转解析|
|`bytecodes.c`|操作码定义 |
|`opcode.py`|操作码元数据 |
|`dis.py`|字节码检查 |

## 25.39 最小心智模型

使用这个模型:```text id="jlwm23"
The compiler walks the AST.
Expressions emit stack-based instructions.
Statements emit side-effect and control-flow instructions.
Constants, names, and locals become indexed table references.
Control flow becomes jumps and exception metadata.
Functions, classes, comprehensions, and generators create nested code objects.
The final instruction stream becomes part of a code object executed by the CPython virtual machine.
```字节码生成是Python语法成为可执行虚拟机操作的阶段。