源码、字节码与执行
源码、字节码与执行
Python 有编译阶段,也有运行阶段。以 CPython 3.12 为例,可以沿着“源码 → AST → code object → 解释器执行”理解一段程序;.pyc 是导入时可能使用的缓存,不是每次执行都必须先产生的文件。
先区分几类对象
代码执行的阶段
CPython 读取源码,解析成 AST,分析作用域并生成 code object/字节码,再由解释器执行。模块可利用有效字节码缓存省掉一部分编译过程;「解释型」不表示没有编译阶段。
字节码与机器码
字节码是解释器理解的指令,机器码直接对应目标 CPU 指令集。CPython 主程序自身由 C 编译器等构建成机器程序;应用 Python 源码编译到字节码是另一个层次,不能混为同一次编译。
pyc 缓存
不是。code object 可只在内存里生成和执行,模块导入常把字节码缓存到 pycache。直接运行主脚本不要求先写出它的 .pyc,缓存目录不可写也不意味着正常源码绝对无法执行。
词法、语法与符号表
词法分析识别名字/字面量/缩进等,解析器组织语法结构,AST 表示程序语义结构,作用域分析判断局部/自由/全局名字。后续生成与优化指令;CPython 3.12 使用 PEG 解析器,具体中间步骤属于实现细节。
代码对象、函数与调用帧
code object 保存编译后的指令及常量等元数据;function 还带全局命名空间、默认参数和闭包等;frame 是一次执行的状态。相同函数递归调用可有多份执行帧,不能把函数对象和某次调用混为一体。
局部名字的静态判定
编译期作用域分析通常会因函数体内给 x 赋值而将其判为局部;实际执行在赋值前读它就失败,不自动退回全局。global/nonlocal 用来明确绑定范围;不是所有名字都完全在运行时随意逐层试。
compile、eval 与 exec
compile 把字符串/AST 生成 code object,不代表执行;eval 执行表达式,exec 执行语句代码。传入自定义 globals 也不构成可靠沙箱,不执行不可信代码;本篇实验只编译和执行固定示例字符串;优化模式可移除 assert,安全校验不能依赖 assert。
模块缓存与字节码缓存
.pyc 是磁盘字节码缓存,正常导入按版本/有效性规则检查;sys.modules 保存本解释器中已加载模块对象。删 .pyc 不会让常驻进程重新执行已导入模块,也不会自动修复模块级缓存持有的大对象。
自适应特化与 JIT
CPython 3.11 起可按运行时类型等信息特化部分字节码,减少通用处理;这不等于项目已启用原生代码 JIT。JIT 把热路径编译到更低层机器执行形式,支持与启用条件依版本/构建;本项目限定 3.12,不能当成已有 JIT。
实验:解析、编译与执行
# 可独立运行
import ast
import dis
source = "result = 1 + 2"
tree = ast.parse(source)
code = compile(tree, "<lesson>", "exec")
namespace = {}
exec(code, namespace)
assert namespace["result"] == 3
assert list(dis.get_instructions(code))示例只执行固定的本地字符串,不把 eval、exec 当成处理不可信输入的办法。字节码指令可能随 Python 版本变化,练习观察阶段关系即可。
进一步的边界
递归限制与底层栈
不是。Python 递归限制是运行时保护,现代 CPython 的 Python 帧与原生调用栈关系也在演进。调高 sys.setrecursionlimit 不保证安全,递归处理超深输入优先改显式栈;不要承诺尾递归自动消除。
AST 成功不等于可执行
AST 主要表示结构,作用域和上下文约束还可能使 compile 失败,例如模块顶层 return;成功编译后也可能有 NameError、导入失败和业务错误。语法解析、编译通过与实际执行正确是三个层次。
字节码缓存的安全边界
不能,字节码和常量可被分析,且依赖解释器格式。缓存校验用于判断是否需要重编,不等同可信来源签名;部署仍需控制包来源和文件权限,不把「没有 .py」当作源码已不可恢复。
性能变化的归因
固定依赖、硬件/配额、输入、并发、预热和构建选项,多次测吞吐、分位延迟、CPU 与内存。编译时间、导入缓存、字节码特化和扩展版本都可能影响;官方基准平均提升不是本项目的实测收益。
字节码与同步保证
不能据此建立业务同步。字节码会随版本变,运行时可能调用用户方法或释放 GIL,复合读改写更不保证原子。需要不变量时用锁或原子接口;free-threaded 下尤其不能依赖旧的 GIL 偶然性。
版本与构建
本组运行原理笔记限定 CPython 3.12。自由线程构建、更新版本的 GC 和 JIT 行为需要分别阅读对应版本文档,不能直接套用这里的机制或把历史核对记录当作当前机器已经实测。
