使用 Nuitka 配合 PyInstaller 打包 Python 程序
在发布 Python 程序时,通常需要将其打包成独立的可执行文件,以便在没有 Python 环境的机器上运行。目前常用的打包工具有 PyInstaller 和 Nuitka。它们各有优缺点:
- PyInstaller: 打包速度快,成功率高,但生成的可执行文件启动速度相对较慢,且代码容易被反编译。
- Nuitka: 通过将 Python 代码编译成 C/C++ 代码来提高执行速度和代码安全性,但打包速度较慢,成功率相对较低,且对某些库的支持可能不如 PyInstaller。
有没有一种方法可以结合两者的优势呢?答案是肯定的。本文将介绍一种使用 Nuitka 将核心 Python 代码编译成 .pyd
(Python 动态链接库) 文件,然后使用 PyInstaller 打包整个程序的方法。这种方法旨在兼顾打包速度、运行性能和代码安全性。
原理
这种方法的原理是利用 Nuitka 的 --module
命令将项目中的大部分 .py
文件编译成一个或多个 .pyd
文件。.pyd
文件是编译后的二进制文件,类似于 Windows 上的 .dll
或 Linux 上的 .so
文件,难以直接反编译,从而提高了代码安全性。同时,编译过程也对代码进行了优化,有助于提升运行性能。
然后,将生成 .pyd
文件作为模块导入到主入口文件(例如 main.py
)中。最后,使用 PyInstaller 打包这个 main.py
文件。由于大部分代码已经被编译成 .pyd
,PyInstaller 需要处理的 Python 源码文件大大减少,从而提高了打包速度和成功率。
准备工作
在开始之前,请确保您的环境中已经安装了 Python、pip,并安装了最新版本的 PyInstaller 和 Nuitka:
1 | pip install pyinstaller nuitka |
假设您的项目结构如下:
1 | your_project/ |
其中 app
文件夹包含了您的主要程序逻辑代码。
操作步骤
第一步:使用 Nuitka 编译核心模块
进入项目根目录,使用 Nuitka 的 --module
命令将 app
文件夹中的所有 .py
文件编译成 .pyd
文件。
1 | # 切换到项目根目录 |
执行此命令后,Nuitka 会在当前目录下生成:
app.cpXX-win_amd64.pyd
(文件名中的XX
取决于您的 Python 版本和系统架构,例如app.cp311-win_amd64.pyd
):这是编译后的二进制模块文件。app.pyi
: 这是一个存根文件,包含了app
模块的导入信息。这个文件对后续 PyInstaller 打包很重要。
第二步:修改主入口文件 (main.py
)
为了让 PyInstaller 能够正确识别和打包 .pyd
模块及其依赖,需要修改 main.py
文件:
- 导入
.pyd
模块: 在main.py
的开头添加一行导入语句,导入刚刚生成的.pyd
模块。例如:1
import app
- 复制
.pyi
中的导入信息: 打开app.pyi
文件,将其中的所有import
语句复制到main.py
的开头。这有助于 PyInstaller 识别所有第三方库依赖。提示: 如果复制的导入语句显得杂乱,可以使用1
2
3
4
5
6
7
8
9
10
11# main.py 开头示例
import app
# 从 app.pyi 复制过来的导入语句
import os
import sys
# ... 其他导入 ...
# 您的主程序逻辑
if __name__ == '__main__':
# 调用 app 模块中的函数或类
app.run_your_app()isort
工具进行排序和去重:1
2pip install isort
isort main.py
修改后的 main.py
应该只包含必要的导入语句和启动程序的核心逻辑,尽量保持简洁。
第三步:使用 PyInstaller 打包
现在,使用 PyInstaller 打包修改后的 main.py
文件。在项目根目录下执行 PyInstaller 命令:
1 | # 使用 PyInstaller 打包 main.py |
PyInstaller 会分析 main.py
,识别到对 app
模块的导入,并将其生成的 .pyd
文件以及所有依赖的第三方库一起打包。
第四步:验证打包结果
打包完成后,在 dist
文件夹中找到生成的可执行文件。运行它,验证程序是否正常工作。
此时,您的核心代码(位于 app
文件夹中)已经被编译成难以反编译的 .pyd
文件,而 main.py
文件内容非常简单,即使被反编译也难以获取核心逻辑。
第五步:程序体积优化 (可选)
打包后的程序体积可能仍然较大。您可以使用一些工具或脚本对打包结果进行进一步优化,例如删除不必要的依赖文件等。这部分内容超出了本文范围,但您可以参考相关的优化指南。
总结
通过结合使用 Nuitka 和 PyInstaller,我们可以:
- 提高代码安全性: 将核心代码编译成二进制
.pyd
文件,增加反编译难度。 - 提升运行性能: Nuitka 的编译优化有助于提高程序执行速度。
- 加快打包速度: PyInstaller 处理的 Python 源码文件减少,打包过程更快。
- 提高打包成功率: 结合 PyInstaller 对复杂依赖的良好支持。
这种混合打包方式继承了 PyInstaller 打包后程序启动速度相对较慢的缺点,但对于需要兼顾性能、安全性和打包效率的场景,是一个非常有效的解决方案。