编译文件 .pyc 与 .pyo
Python 是解释型语言,但为了提高加载速度,会把 .py 源文件编译成字节码(bytecode)缓存。Python 2 中,这些缓存文件与源文件放在同一目录,扩展名为 .pyc 或 .pyo。
.pyc 文件
当模块第一次被导入时,Python 把源代码编译成字节码,保存为 .pyc 文件:
myproject/
hello.py
hello.pyc # 编译后的字节码
下次导入时,如果 .pyc 的修改时间比 .py 新,Python 直接加载 .pyc,跳过编译步骤,加快启动速度。
import hello # 第一次:编译并保存 hello.pyc
import hello # 第二次:直接加载 hello.pyc
.pyo 文件
.pyo 是优化过的字节码文件,用 python -O 或 python -OO 生成:
$ python -O script.py # 生成 .pyo,移除 assert 语句
$ python -OO script.py # 生成 .pyo,移除 assert 和 docstring
-O 优化级别:
- 移除
assert语句 - 设置
__debug__ = False
-OO 优化级别:
- 包含
-O的所有优化 - 移除文档字符串(
__doc__为None)
# test_assert.py
assert 1 == 2, "This should fail"
# 普通运行
$ python test_assert.py
AssertionError: This should fail
# -O 运行
$ python -O test_assert.py
# 无输出,assert 被移除了
编译文件的位置
Python 2 中,.pyc 和 .pyo 与 .py 源文件放在同一目录:
project/
module1.py
module1.pyc
module2.py
module2.pyo
package/
__init__.py
__init__.pyc
submod.py
submod.pyc
这会导致目录杂乱,也是 Python 3 改为 __pycache__ 子目录的原因之一。
不生成 .pyc
可以用 -B 选项或设置 PYTHONDONTWRITEBYTECODE 环境变量禁止生成 .pyc:
$ python -B script.py
# 或
$ export PYTHONDONTWRITEBITECODE=1
$ python script.py
也可以在代码中设置:
import sys
sys.dont_write_bytecode = True
这在开发调试时有用,避免修改代码后加载过期的 .pyc。
.pyc 的局限性
.pyc 只加速加载速度,不加速执行速度。字节码在运行时仍需由 Python 虚拟机解释执行,没有编译成机器码。
如果删除 .py 源文件,只保留 .pyc,模块仍然可以导入:
$ rm hello.py
$ python -c "import hello; hello.greet()" # 可以运行!
但这只是加载 .pyc 中的字节码,无法查看或修改源代码。
与 Python 3 的对比
| 特性 | Python 2 | Python 3 |
|---|---|---|
| 缓存位置 | 与 .py 同目录 | __pycache__/ 子目录 |
| 文件名 | .pyc / .pyo | __pycache__/module.cpython-37.pyc |
| 优化级别 | -O / -OO | 相同 |
Python 3 的 __pycache__ 设计更干净,支持多版本共存(Python 3.7 和 3.8 的 .pyc 可以放在同一目录的不同子目录中)。