不能。.pyc文件无法真正隐藏python源码,uncompyle6等工具可快速反编译还原代码;其仅能阻止非专业查看者,需结合pyarmor混淆、cython编译或运行时解密等手段提升逆向门槛。

Python发布时能靠.pyc文件真正隐藏源码吗?
不能。直接分发.pyc文件对防逆向几乎无效——uncompyle6、decompyle3等工具几秒就能还原出接近原始的.py代码,变量名、逻辑结构、注释(若未被优化掉)都可能恢复。所谓“隐藏”,只拦得住不想折腾的 casual 查看者。
如果你的目标是提高逆向门槛或满足内部合规要求(比如禁止明文分发),.pyc本身不是解法,而是混淆或打包流程中的一个中间产物。关键在后续处理环节是否引入了真正的干扰。
用compileall生成.pyc的常见误区
很多人用python -m compileall生成__pycache__下的.pyc,然后手动复制出来发布——这不仅没隐藏源码,还引入了路径和版本绑定问题:
-
.pyc文件头包含 magic number,对应 Python 解释器版本(如3.9的 magic 是0x3412),换版本运行直接报ImportError: bad magic number - 文件路径信息被硬编码进
.pyc的co_filename字段,出错时仍会暴露原始路径 - 未启用
-OO优化时,assert和__doc__全部保留,docstring 就是功能说明书
正确做法是:在目标环境上用对应 Python 版本执行编译,并加 -OO:
python -OO -m compileall -b -f mypackage/
-b 强制写入同目录 .pyc(不进 __pycache__),-f 强制重编译,-OO 同时移除 assert 和 __doc__ ——这是最基础但常被跳过的一步。
真正提升混淆强度的三个实操方向
单靠 .pyc 不行,但可以组合以下手段增加静态分析成本:
- 用
pyarmor对.py源码做字节码级混淆:它不只加密字符串,还会动态打乱代码执行顺序、插入无意义跳转、替换常量为运行时计算值。生成的.pyc必须配合其提供的pytransform运行时才能加载,且支持绑定机器指纹或过期时间 - 用
cython将关键模块编译为.so/.pyd:C 扩展天然脱离 Python 字节码体系,反编译需进入 C 层,且可开启 GCC 优化(-O3)、符号剥离(strip)进一步模糊逻辑 - 把主逻辑拆进资源文件 + 运行时解密加载:例如将 AES 加密后的代码存为
data.bin,启动时用硬编码密钥解密并exec(compile(...))。虽然密钥仍可能被内存 dump 提取,但已过滤掉静态扫描类攻击
注意:pyinstaller --onefile 默认不混淆,只是打包;加 --key 参数仅加密打包内资源,不改变 Python 字节码本身——这点常被误解。
为什么你写的setup.py打包后还是能看到.py?
因为默认 setuptools 的 sdist 或 bdist_wheel 都会把 py_modules 或 packages 列出的源文件原样塞进分发包。即使你本地删了 .py,只要 setup.py 里写了 packages=['mymodule'],它就会去读目录里的 .py 文件。
要让 wheel 里只有 .pyc,必须绕过默认行为:
- 先用
python -OO -m compileall编译出干净的.pyc - 手动构建
MANIFEST.in,只 include**/*.pyc,exclude**/*.py - 在
setup.py中设置include_package_data=True,并禁用自动发现:packages=find_packages(where='.', exclude=['tests*'])改为显式列出packages=['mymodule'],再确保该目录下只有.pyc
更稳妥的做法是放弃 setup.py 流程,直接用 zipapp 或 pyinstaller 构建独立归档——它们不依赖安装逻辑,天然规避源码残留问题。
混淆强度永远是时间和成本的函数:越强的保护,越可能影响调试、热更新、甚至某些 IDE 的跳转支持。真正需要防的不是技术高手,而是防止代码被直接复制挪用。盯住你的威胁模型,别在 .pyc 上过度设计。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











