使用 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
2
3
4
5
6
your_project/
├── main.py
└── app/
├── __init__.py
└── your_module.py
└── another_module.py

其中 app 文件夹包含了您的主要程序逻辑代码。

操作步骤

第一步:使用 Nuitka 编译核心模块

进入项目根目录,使用 Nuitka 的 --module 命令将 app 文件夹中的所有 .py 文件编译成 .pyd 文件。

1
2
3
4
5
6
7
# 切换到项目根目录
cd your_project

# 使用 nuitka 编译 app 模块
# --module 表示编译为模块
# --include-package=app 确保包含 app 包内的所有内容
python -m nuitka --module app --include-package=app

执行此命令后,Nuitka 会在当前目录下生成:

  1. app.cpXX-win_amd64.pyd (文件名中的 XX 取决于您的 Python 版本和系统架构,例如 app.cp311-win_amd64.pyd):这是编译后的二进制模块文件。
  2. app.pyi: 这是一个存根文件,包含了 app 模块的导入信息。这个文件对后续 PyInstaller 打包很重要。

第二步:修改主入口文件 (main.py)

为了让 PyInstaller 能够正确识别和打包 .pyd 模块及其依赖,需要修改 main.py 文件:

  1. 导入 .pyd 模块: 在 main.py 的开头添加一行导入语句,导入刚刚生成的 .pyd 模块。例如:
    1
    import app
  2. 复制 .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
    2
    pip install isort
    isort main.py

修改后的 main.py 应该只包含必要的导入语句和启动程序的核心逻辑,尽量保持简洁。

第三步:使用 PyInstaller 打包

现在,使用 PyInstaller 打包修改后的 main.py 文件。在项目根目录下执行 PyInstaller 命令:

1
2
3
# 使用 PyInstaller 打包 main.py
# 根据需要添加 PyInstaller 的参数,例如 --onefile, --windowed 等
pyinstaller main.py --clean --noconfirm

PyInstaller 会分析 main.py,识别到对 app 模块的导入,并将其生成的 .pyd 文件以及所有依赖的第三方库一起打包。

第四步:验证打包结果

打包完成后,在 dist 文件夹中找到生成的可执行文件。运行它,验证程序是否正常工作。

此时,您的核心代码(位于 app 文件夹中)已经被编译成难以反编译的 .pyd 文件,而 main.py 文件内容非常简单,即使被反编译也难以获取核心逻辑。

第五步:程序体积优化 (可选)

打包后的程序体积可能仍然较大。您可以使用一些工具或脚本对打包结果进行进一步优化,例如删除不必要的依赖文件等。这部分内容超出了本文范围,但您可以参考相关的优化指南。

总结

通过结合使用 Nuitka 和 PyInstaller,我们可以:

  • 提高代码安全性: 将核心代码编译成二进制 .pyd 文件,增加反编译难度。
  • 提升运行性能: Nuitka 的编译优化有助于提高程序执行速度。
  • 加快打包速度: PyInstaller 处理的 Python 源码文件减少,打包过程更快。
  • 提高打包成功率: 结合 PyInstaller 对复杂依赖的良好支持。

这种混合打包方式继承了 PyInstaller 打包后程序启动速度相对较慢的缺点,但对于需要兼顾性能、安全性和打包效率的场景,是一个非常有效的解决方案。

更多资源


© 2025 vmoranv 使用 Stellar 创建


😊本站2025.05.05日起🎉累计访问人次💻


614447.xyz