page contents

Python 中的 .pyc 文件:按字节码编译的模块缓存

.pyc 可以理解为 Python 源文件的一种“中间形式”缓存。下一次从其他程序导入同一个模块时,如果缓存仍然有效,Python 就可以跳过部分解析和编译工作,从而更快地完成导入。

attachments-2026-10-yJ28Accu6adffeb039f8b.png

Python 中的 .pyc 文件:按字节码编译的模块缓存

一、为什么需要 .pyc 文件

导入一个模块并不是“零成本”的操作。Python 在 import 一个模块时,通常需要:

找到模块对应的源文件,例如 mymodule.py;

读取源文件;

把源代码解析成抽象语法树;

编译成 Python 字节码;

由 Python 虚拟机执行这些字节码。

其中“解析”和“编译”会消耗时间。如果一个模块被多个程序反复导入,每次都重新编译显然不划算。因此 Python 引入了一种缓存机制:把编译后的字节码保存到文件中,这个文件通常以 .pyc 为扩展名。

.pyc 可以理解为 Python 源文件的一种“中间形式”缓存。下一次从其他程序导入同一个模块时,如果缓存仍然有效,Python 就可以跳过部分解析和编译工作,从而更快地完成导入。

需要注意:.pyc 中保存的是 Python 字节码,不是机器码,也不是把 Python 程序彻底编译成原生可执行文件。它仍然需要 Python 解释器/虚拟机来执行。

二、.pyc 文件是如何产生的

假设有一个模块文件 mymodule.py:

# mymodule.py
def greet(name):
    return f"Hello, {name}!"

print("mymodule 被加载了")

在另一个文件中导入它:

# main.py
import mymodule

print(mymodule.greet("Python"))

第一次导入时,如果条件允许,Python 会在源文件附近生成字节码缓存文件。在 Python 3 中,通常不是直接放在 mymodule.py 旁边,而是放在 __pycache__ 目录中,文件名类似:

__pycache__/mymodule.cpython-311.pyc

其中 cpython-311 表示这是 CPython 3.11 生成的字节码缓存。不同 Python 实现、不同 Python 版本可能使用不同文件名,以避免字节码格式不兼容。

在较老的 Python 版本或某些资料中,会说 .pyc 文件创建在与 .py 文件相同的目录中。更准确地说:Python 会在源文件所在目录附近创建缓存;Python 3 默认使用 __pycache__ 子目录。如果 Python 没有权限向该目录写入文件,那么 .pyc 文件就不会被创建,但这通常不会影响程序运行,只是下次导入时仍然需要重新编译。

三、.pyc 为什么能加快导入

当再次导入模块时,Python 会检查:

是否存在对应的 .pyc 文件;

.pyc 是否与当前 Python 版本兼容;

源文件是否发生变化;

编译选项、优化级别等是否匹配。

如果缓存有效,Python 可以直接加载字节码,而不必重新完成完整的解析和编译过程。这样导入会更快。

可以用一个简单实验观察。先删除缓存:

rm -rf __pycache__

然后运行:

python -c "import mymodule"

之后再运行一次:

python -c "import mymodule"

第二次导入时通常可以使用已经生成的 .pyc 缓存。

不过要注意,.pyc 主要优化的是“编译阶段”的开销。模块顶层代码仍然会在导入时执行。例如 mymodule.py 中的:

print("mymodule 被加载了")

每次在新进程中导入模块时仍会执行。.pyc 不会让模块顶层逻辑完全消失。

四、.pyc 是平台独立的吗?

通常可以说 .pyc 文件是独立于运行平台的,因为它保存的是 Python 字节码,而不是特定 CPU 的机器指令。因此,同一个 .pyc 文件在不同操作系统上,只要 Python 实现和字节码版本兼容,理论上可以复用。

但“平台独立”不等于“跨所有 Python 版本通用”。.pyc 与 Python 的字节码版本密切相关。例如 Python 3.10 和 Python 3.11 的字节码格式可能不同,因此不能随意混用。Python 通过文件名中的版本标记、文件头中的魔术数等信息来检查兼容性。

所以更准确的说法是:

.pyc 不绑定具体操作系统或 CPU 架构,但绑定 Python 实现和字节码版本。

五、手动生成 .pyc

可以使用标准库 py_compile 手动编译一个源文件:

import py_compile

py_compile.compile("mymodule.py")

也可以指定输出路径:

py_compile.compile("mymodule.py", cfile="mymodule.pyc")

如果要批量编译一个目录,可以使用 compileall:

python -m compileall .

在代码中也可以:

import compileall

compileall.compile_dir(".")

六、查看 .pyc 中的字节码

.pyc 中保存的是字节码,可以用 dis 模块反汇编查看。例如:

import dis
import mymodule

dis.dis(mymodule.greet)

输出会显示类似 LOAD_FAST、FORMAT_VALUE、RETURN_VALUE 等字节码指令。这有助于理解 Python 虚拟机是如何执行函数的。

也可以直接反汇编源码字符串:

import dis

dis.dis("x = 1 + 2")

七、如何禁止生成 .pyc

有时我们不希望生成缓存文件,例如只读文件系统、容器镜像或临时脚本场景。可以使用:

python -B main.py

-B 表示不写入 .pyc 文件。也可以设置环境变量:

export PYTHONDONTWRITEBYTECODE=1

或者在代码中设置:

import sys
sys.dont_write_bytecode = True

注意,这些设置主要影响是否写入新的 .pyc,并不一定阻止读取已有缓存。

八、常见误区

1. .pyc 不是加密文件

.pyc 只是字节码缓存,可以被反编译或分析,不能用来保护源码。如果需要保护代码,应考虑其他方案,而不是依赖 .pyc。

2. .pyc 不是可执行文件

.pyc 不能像原生可执行程序那样独立运行,仍然需要 Python 解释器。

3. 删除 .pyc 通常不会破坏程序

删除 __pycache__ 或 .pyc 文件后,Python 通常会在需要时重新生成,或者退回到直接编译源文件。

4. 没有写权限时程序仍可运行

如果目录不可写,Python 无法创建 .pyc,但通常仍能从 .py 源文件导入模块,只是少了缓存加速。

九、总结

.pyc 文件是 Python 为了加速模块导入而引入的字节码缓存。它把源文件编译后的中间形式保存下来,使下一次导入时可以跳过部分解析和编译工作。Python 3 通常把缓存放在 __pycache__ 目录中,文件名带有 Python 实现和版本标记。.pyc 保存的是 Python 字节码,不是机器码;它通常与操作系统平台无关,但与 Python 字节码版本相关。如果目录没有写权限,.pyc 不会被创建,但程序通常仍可正常运行。

一句话概括:

.pyc 是 Python 模块的字节码缓存文件,用来减少重复编译开销,加快后续导入速度,但它不是加密文件,也不是跨所有 Python 版本通用的可执行文件。

更多相关技术内容咨询欢迎前往并持续关注好学星城论坛了解详情。

想高效系统的学习Python编程语言,推荐大家关注一个微信公众号:Python编程学习圈。每天分享行业资讯、技术干货供大家阅读,关注即可免费领取整套Python入门到进阶的学习资料以及教程,感兴趣的小伙伴赶紧行动起来吧。

attachments-2022-05-rLS4AIF8628ee5f3b7e12.jpg

 

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

2541 篇文章

作家榜 »

  1. Pack 2541 文章
  2. 轩辕小不懂 2403 文章
  3. 小柒 2228 文章
  4. Nen 576 文章
  5. 王昭君 216 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章