704 字
2 分钟
Python 解释器原理:CPython 执行流程
凌晨两点,线上服务突然内存飙升。你打开监控,发现 Python 进程的 RSS 已经是启动时的三倍,但业务日志里没有任何异常。tracemalloc 报告显示对象数量在持续增长——你意识到这不是业务逻辑的问题,而是 Python 内存管理的”黑箱”在作祟。要真正理解这个问题,必须回到起点:Python 代码到底是怎么被执行的?
CPython 执行流程概览
flowchart TB
A[Python 源码] --> B[词法分析 Lexer]
B --> C[Token 流]
C --> D[语法分析 Parser]
D --> E[AST 抽象语法树]
E --> F[编译器 Compiler]
F --> G[字节码 Bytecode]
G --> H[Python 虚拟机]
H --> I[执行结果]
一、词法分析
1.1 Token 类型
Python 将源码分解为 Token(词法单元):
# 源码x = 1 + 2
# Token 流NAME 'x' (1, 0)EQUAL '=' (1, 2)NUMBER '1' (1, 4)PLUS '+' (1, 6)NUMBER '2' (1, 8)NEWLINE '' (1, 9)主要 Token 类型:
| 类型 | 示例 |
|---|---|
| NAME | x, print, if |
| NUMBER | 123, 3.14, 0xff |
| STRING | ’hello’, “world” |
| KEYWORD | if, else, for, while |
| OP | +, -, *, /, = |
| NEWLINE | \n |
| INDENT | 缩进 |
| DEDENT | 减少缩进 |
1.2 词法分析过程
// Python 词法分析器(简化)static int tok_get(tok_state *tok, char **p_start, char **p_end) { char c;
// 跳过空白和注释 while (true) { c = tok_nextc(tok); if (c == ' ' || c == '\t') continue; if (c == '#') { while (c != '\n') c = tok_nextc(tok); } break; }
// 识别标识符 if (isalpha(c) || c == '_') { return NAME; }
// 识别数字 if (isdigit(c)) { return NUMBER; }
// 识别字符串 if (c == '"' || c == '\'') { return STRING; }
// ...}二、语法分析
2.1 AST 结构
Python 使用 AST(Abstract Syntax Tree,抽象语法树) 表示程序结构:
# 源码def add(a, b): return a + b
result = add(1, 2)# AST(简化表示)Module(body=[ FunctionDef( name='add', args=arguments(args=[ arg(arg='a'), arg(arg='b') ]), body=[ Return(value=BinOp( left=Name(id='a'), op=Add(), right=Name(id='b') )) ] ), Assign( targets=[Name(id='result')], value=Call( func=Name(id='add'), args=[Num(n=1), Num(n=2)] ) )])2.2 查看 AST
import ast
code = """def add(a, b): return a + b"""
tree = ast.parse(code)print(ast.dump(tree, indent=2))2.3 AST 节点类型
flowchart TB
A[AST Node] --> B[语句 stmt]
A --> C[表达式 expr]
A --> D[其他]
B --> B1[FunctionDef]
B --> B2[ClassDef]
B --> B3[Return]
B --> B4[Assign]
B --> B5[If]
B --> B6[For]
B --> B7[While]
C --> C1[BinOp]
C --> C2[Compare]
C --> C3[Call]
C --> C4[Name]
C --> C5[Num]
C --> C6[Str]
D --> D1[arguments]
D --> D2[arg]
D --> D3[keyword]
三、编译阶段
3.1 字节码
Python 字节码是栈式虚拟机指令:
# 源码def add(a, b): return a + b
# 字节码import disdis.dis(add) 2 0 LOAD_FAST 0 (a) 2 LOAD_FAST 1 (b) 4 BINARY_ADD 6 RETURN_VALUE3.2 常用字节码指令
| 指令 | 操作 |
|---|---|
| LOAD_CONST | 加载常量到栈 |
| LOAD_FAST | 加载局部变量到栈 |
| LOAD_NAME | 加载名字到栈 |
| STORE_FAST | 存储到局部变量 |
| BINARY_ADD | 弹出两个值,相加,结果入栈 |
| BINARY_SUBTRACT | 弹出两个值,相减 |
| BINARY_MULTIPLY | 弹出两个值,相乘 |
| COMPARE_OP | 比较操作 |
| POP_JUMP_IF_FALSE | 条件跳转 |
| JUMP_FORWARD | 无条件跳转 |
| CALL_FUNCTION | 调用函数 |
| RETURN_VALUE | 返回栈顶值 |
3.3 编译过程
flowchart TB
A[AST] --> B[符号表构建]
B --> C[作用域分析]
C --> D[字节码生成]
D --> E[优化]
E --> F[Code Object]
subgraph 符号表
B1[局部变量]
B2[自由变量]
B3[全局变量]
B4[内置变量]
end
// 编译 AST 到字节码(简化)static int compiler_visit_expr(compiler *c, expr_ty e) { switch (e->kind) { case BinOp_kind: // 编译左操作数 compiler_visit_expr(c, e->v.BinOp.left); // 编译右操作数 compiler_visit_expr(c, e->v.BinOp.right); // 生成操作指令 compiler_addop(c, binop_opcode(e->v.BinOp.op)); break; case Name_kind: // 加载变量 compiler_addop_name(c, LOAD_NAME, e->v.Name.id); break; // ... }}3.4 Code Object
def func(x): y = x + 1 return y
# 查看 Code Objectprint(func.__code__.co_code) # 字节码print(func.__code__.co_varnames) # 局部变量名print(func.__code__.co_consts) # 常量print(func.__code__.co_names) # 名称print(func.__code__.co_filename) # 文件名print(func.__code__.co_firstlineno) # 起始行号四、Python 虚拟机
4.1 执行模型
Python 虚拟机是栈式虚拟机:
flowchart LR
subgraph 栈帧 Frame
A[数据栈<br/>Value Stack]
B[块栈<br/>Block Stack]
C[局部变量<br/>Locals]
end
D[字节码] --> A
A --> E[执行结果]
4.2 栈帧结构
// PyFrameObject 结构(简化)typedef struct _PyInterpreterFrame { PyObject *f_func; // 函数对象 PyObject *f_locals; // 局部变量 PyObject *f_globals; // 全局变量 PyObject *f_builtins; // 内置函数
PyCodeObject *f_code; // 代码对象 int f_lasti; // 上条指令位置
// 数据栈 PyObject **stackpointer; PyObject **stacktop;
// 块栈(用于异常、循环) PyTryBlock f_blockstack[CO_MAXBLOCKS];} PyInterpreterFrame;4.3 执行循环
// Python 虚拟机主循环(简化)PyObject* _PyEval_EvalFrameDefault(PyThreadState *tstate, PyInterpreterFrame *frame) { PyObject **stack_pointer = frame->stacktop;
// 主执行循环 for (;;) { // 获取指令 _Py_CODEUNIT *next_instr = frame->instr_ptr++; unsigned char opcode = _Py_OPCODE(*next_instr);
switch (opcode) { case LOAD_CONST: { PyObject *value = GETITEM(frame->code->co_consts, oparg); Py_INCREF(value); PUSH(value); break; }
case LOAD_FAST: { PyObject *value = GETLOCAL(oparg); if (value == NULL) { // 未初始化变量错误 } Py_INCREF(value); PUSH(value); break; }
case BINARY_ADD: { PyObject *right = POP(); PyObject *left = TOP(); PyObject *sum = PyNumber_Add(left, right); SET_TOP(sum); Py_DECREF(left); Py_DECREF(right); break; }
case RETURN_VALUE: { PyObject *value = POP(); return value; }
// ... 更多指令处理 } }}4.4 栈操作示例
# 代码x = 1 + 2
# 字节码执行过程# 1. LOAD_CONST 1# 栈: [1]# 2. LOAD_CONST 2# 栈: [1, 2]# 3. BINARY_ADD# 弹出 1, 2,计算 1+2=3,压入 3# 栈: [3]# 4. STORE_NAME 'x'# 弹出 3,存入 x# 栈: []sequenceDiagram
participant VM as 虚拟机
participant Stack as 数据栈
participant Locals as 局部变量
VM->>Stack: LOAD_CONST 1
Note over Stack: [1]
VM->>Stack: LOAD_CONST 2
Note over Stack: [1, 2]
VM->>Stack: BINARY_ADD
Note over Stack: [3]
VM->>Locals: STORE_NAME x
Note over Locals: x=3
Note over Stack: []
五、内存管理
5.1 内存分配层次
flowchart TB
A[Python 对象] --> B[PyMalloc<br/>小对象分配器]
B --> C[系统内存池<br/>Arena/Pool]
C --> D[操作系统<br/>malloc/free]
E[大对象<br/>>512B] --> D
5.2 PyMalloc
Python 使用自己的内存分配器管理小对象:
// 内存池结构struct pool_header { union { block *_padding; } pad; block *freeblock; // 空闲块链表 struct pool_header *nextpool; struct pool_header *prevpool; uint arenaindex; // Arena 索引 uint szidx; // 块大小索引 uint refcount; // 已分配块数};
struct arena_object { uptr address; // Arena 起始地址 block* pool_address; // Pool 数组 uint nfreepools; // 空闲 Pool 数 uint ntotalpools; // 总 Pool 数};内存分配策略:
| 对象大小 | 分配方式 |
|---|---|
| < 512B | PyMalloc 从 Pool 分配 |
| >= 512B | 直接 malloc |
5.3 垃圾回收
Python 使用 引用计数(Reference Counting) + 循环垃圾回收:
// 引用计数#define Py_INCREF(op) ((void)(++(op)->ob_refcnt))#define Py_DECREF(op) \ do { \ if (--((op)->ob_refcnt) == 0) \ _Py_Dealloc((PyObject *)(op)); \ } while (0)flowchart TB
A[对象创建] --> B[引用计数 = 1]
B --> C{引用变化}
C -->|引用增加| D[计数 + 1]
C -->|引用减少| E[计数 - 1]
D --> C
E --> F{计数 = 0?}
F -->|是| G[立即释放]
F -->|否| C
H[循环引用] --> I[引用计数无法回收]
I --> J[循环 GC 标记清除]
循环垃圾回收:
import gc
# 开启自动 GCgc.enable()
# 手动触发 GCgc.collect()
# 查看 GC 状态gc.get_stats()六、对象系统
6.1 PyObject 结构
所有 Python 对象的基础:
// PyObject 结构typedef struct _object { Py_ssize_t ob_refcnt; // 引用计数 PyTypeObject *ob_type; // 类型指针} PyObject;
// 变长对象typedef struct { PyObject ob_base; Py_ssize_t ob_size; // 元素个数} PyVarObject;6.2 类型对象
// PyTypeObject 结构(简化)typedef struct _typeobject { PyObject_VAR_HEAD const char *tp_name; // 类型名 Py_ssize_t tp_basicsize; // 基本大小
// 方法 destructor tp_dealloc; // 析构函数 printfunc tp_print; // 打印函数 getattrfunc tp_getattro; // 属性获取 setattrfunc tp_setattro; // 属性设置
// 数字方法 binaryfunc nb_add; binaryfunc nb_subtract; binaryfunc nb_multiply;
// 序列方法 lenfunc sq_length; binaryfunc sq_concat; ssizeargfunc sq_item;
// 映射方法 lenfunc mp_length; binaryfunc mp_subscript;
// ...} PyTypeObject;6.3 对象创建
// 创建整数对象PyObject *PyLong_FromLong(long v) { PyLongObject *result;
// 小整数缓存 if (-5 <= v && v <= 256) { return PyLong_FromSmallInt(v); }
// 分配并初始化 result = PyObject_MALLOC(sizeof(PyLongObject)); if (!result) return NULL;
result->ob_base.ob_refcnt = 1; result->ob_base.ob_type = &PyLong_Type; result->ob_digit[0] = v;
return (PyObject *)result;}七、函数与闭包
7.1 函数对象
def add(a, b): return a + b
# 函数对象属性print(add.__code__) # 代码对象print(add.__globals__) # 全局变量print(add.__defaults__) # 默认参数print(add.__closure__) # 闭包变量7.2 闭包实现
def outer(x): def inner(y): return x + y return inner
func = outer(10)func(5) # 返回 15# 闭包字节码dis.dis(func) 2 0 LOAD_DEREF 0 (x) 2 LOAD_FAST 0 (y) 4 BINARY_ADD 6 RETURN_VALUE闭包数据结构:
// 闭包单元typedef struct { PyObject_HEAD PyObject *ob_ref; // 捕获的变量} PyCellObject;
// 函数对象typedef struct { PyObject_HEAD PyCodeObject *func_code; // 代码对象 PyObject *func_globals; // 全局变量 PyObject *func_defaults; // 默认参数 PyObject *func_kwdefaults; // 关键字默认参数 PyObject *func_closure; // 闭包单元元组} PyFunctionObject;八、性能优化
8.1 字节码缓存
# Python 会缓存编译后的字节码# .py 文件 -> .pyc 文件
# 缓存位置__pycache__/ module.cpython-311.pyc8.2 小整数缓存
# 小整数 (-5, 256) 被缓存a = 256b = 256print(a is b) # True
a = 257b = 257print(a is b) # False (新对象)8.3 字符串驻留
# 字符串自动驻留a = "hello"b = "hello"print(a is b) # True
a = "hello world"b = "hello world"print(a is b) # 可能 False (包含空格)8.4 使用 dis 模块分析
import dis
# 分析函数字节码def example(): x = [i * 2 for i in range(10)] return x
dis.dis(example)
# 查看字节码详细信息dis.Bytecode(example).info()九、性能调试
9.1 cProfile 分析
import cProfile
def slow_function(): total = 0 for i in range(100000): total += i return total
cProfile.run('slow_function()', sort='cumulative')9.2 sys.monitoring (Python 3.12+)
import sys.monitoring as monitor
# 设置监控monitor.use_tool_id(monitor.PROFILER_ID, "my_profiler")
# 注册事件处理器@monitor.register_callbackdef on_call(code, offset): print(f"Called {code.co_name} at offset {offset}")9.3 内存分析
import sys
# 查看对象大小x = [i for i in range(1000)]print(sys.getsizeof(x)) # 列表容器大小
# 使用 tracemalloc 追踪内存import tracemalloctracemalloc.start()
# ... 代码 ...
snapshot = tracemalloc.take_snapshot()top_stats = snapshot.statistics('lineno')for stat in top_stats[:10]: print(stat)关键要点
- 词法分析:源码转化为 Token 流
- 语法分析:Token 流转化为 AST(Abstract Syntax Tree,抽象语法树)
- 编译:AST 转化为字节码
- 执行:字节码由栈式虚拟机执行
- 内存管理:引用计数(Reference Counting) + 循环 GC
- 对象系统:PyObject 统一接口
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
Python 解释器原理:CPython 执行流程
https://blog.souloss.com/posts/principles/principles-python-interpreter/ 部分信息可能已经过时
相关文章 智能推荐
1
Go 编译器深入
编译器 深入 Go 编译器的设计与实现——编译流程、SSA 中端、逃逸分析、Go GC 与编译器协作——Go 如何在编译速度和代码质量之间取得独特平衡。
2
Rust 编译器与借用检查器
编译器 深入 Rust 编译器——rustc 架构、HIR→MIR→LLVM IR 编译流程、借用检查器在 MIR 上的实现、生命周期推导、宏展开,理解 Rust 如何在编译期保证内存安全。
3
WebAssembly 编译
编译器 深入 WebAssembly 编译——WASM 指令集、Cranelift 代码生成、WASI 标准、从 Rust/C 到 WASM 的编译流程、WASM 运行时,理解 Web 如何成为通用编译目标。
4
系列导读
编译器 本系列从编译器前端到后端、从静态编译到 JIT 动态编译、从经典优化到 AI 驱动优化,全面深入现代编译器与语言运行时的核心机制——词法分析、语法分析、语义分析、IR 与 SSA、优化、代码生成、LLVM 架构、JIT、GC、V8/Go/Rust 编译器、WASM、AI 编译优化,每章配有可运行的代码实验,让你从「会用编译器」进阶到「理解编译器」。
5
综合实战:构建一个迷你编程语言
编译器 综合实战——设计一门小语言,实现完整编译器——词法分析→语法分析→IR→优化→代码生成,运用前 18 章知识从零构建编译器。






