Python先将源码编译为字节码再由PVM执行;.pyc文件在运行时自动生成于__pycache__目录,可手动编译或禁用缓存;dis模块可反汇编查看LOAD_FAST、BINARY_ADD等字节码指令。

Python 是解释型语言,但它的执行过程不是直接逐行翻译源码,而是先编译为字节码(bytecode),再由 Python 虚拟机(PVM)解释执行。理解这个流程,有助于调试性能问题、理解 import 机制,甚至安全审计。
源代码 → 字节码:pyc 文件的生成时机
当你运行 python script.py,Python 解释器会先检查是否存在对应 .pyc 文件(位于 __pycache__ 目录下,命名如 script.cpython-311.pyc)。若存在且时间戳新于源文件,则直接加载字节码;否则,重新编译源码生成字节码并缓存。
- 手动触发编译可用 compile() 函数或 py_compile 模块
- 使用 -B 参数运行可跳过写入 .pyc(如 python -B script.py)
- 设置环境变量 PYTHONDONTWRITEBYTECODE=1 可全局禁用缓存
字节码结构:dis 模块查看与解读
字节码是 Python 虚拟机可识别的低级指令序列,每条指令通常含操作码(opcode)和参数(arg)。用内置模块 dis 可反汇编函数或模块:
import dis
def add(a, b):
return a + b
dis.dis(add)
输出类似:
立即学习“Python免费学习笔记(深入)”;
2 0 LOAD_FAST 0 (a)
2 LOAD_FAST 1 (b)
4 BINARY_ADD
6 RETURN_VALUE
- LOAD_FAST 表示从局部变量栈快速加载变量
- BINARY_ADD 是实际执行加法的操作码
Python 虚拟机:如何执行字节码
PVM 是一个循环解释器,核心是一个“取指-解码-执行”循环(fetch-decode-execute loop)。它维护多个运行时结构:
- 帧对象(frame object):每个函数调用创建一个帧,保存局部变量、指令指针、异常上下文等
- 求值栈(evaluation stack):大多数操作码(如 LOAD、BINARY_*)在此压栈/弹栈
- 常量表(co_consts)、名称表(co_names)、变量表(co_varnames):存储字节码引用的常量、全局名、局部名等,提升查找效率
为什么不是纯解释?JIT 呢?
标准 CPython 不含 JIT 编译器,字节码始终由 PVM 解释执行。这是它相比 PyPy(自带 JIT)、Numba(针对数值函数 JIT)或 Cython(预编译为 C)的主要性能差异来源。不过,CPython 3.11 引入了自适应字节码优化(如特殊化指令),在不改变模型的前提下小幅提速。
字节码层面对应的是语言语义,而非硬件指令,因此跨平台——同一份 .pyc 在不同操作系统上只要 Python 版本兼容就能运行(注意:3.11 的 pyc 不兼容 3.10)。










