几乎每个 Python 开发者在学习过程中都会思考一个问题:如何将自己编写的程序方便地分享给他人运行?
与 C++ 代码可以直接编译成可执行文件,或 JavaScript 代码能在浏览器中直接运行不同,Python 代码必须依赖 Python 解释器来执行。然而,很多操作系统并没有预装 Python 环境。

因此,一个直接的想法是先将 Python 环境安装到目标电脑上。但如果这个方法行不通,另一个更优雅的解决方案是使用工具将 Python 脚本打包成一个独立的可执行文件。
市面上有多种 Python 打包工具,PyInstaller 是其中广为人知的一个。不过,许多开发者同时也推荐另一个名字:Nuitka。那么,Nuitka 究竟有何特别之处?它又如何使用呢?
Nuitka 的基本安装与打包
安装 Nuitka 非常简单,只需使用 pip 命令:
pip install nuitka
安装完成后,通过 nuitka 命令加上目标 Python 文件名即可进行基础打包:
nuitka love.py

但是,请注意,以上述简单方式打包生成的 .exe 文件,仍然无法在没有安装 Python 的计算机上运行。因为它只包含了主程序,没有将 Python 解释器及其依赖一并打包。
为了实现真正的独立运行,我们需要使用 --standalone 参数:
nuitka 你的程序.py --standalone
更进一步,如果你希望最终产物是一个单独的文件,而非一个包含众多依赖的文件夹,可以再加上 --onefile 参数:
nuitka 你的程序.py --standalone --onefile
这样生成的程序就可以在没有安装 Python 的同类型系统上运行了。例如,在 Windows 7 下打包的程序,通常也能在 Windows 10 上正常运行。反过来则不一定可行,原因在于系统底层的兼容性。

打包包含外部资源的复杂项目
理论讲完了,让我们实战一下。以之前一个名为“羊了个羊”的 Python 小游戏为例,它使用了 pgzero 库,并包含了图片、音效等外部资源,看看 Nuitka 对复杂 GUI 程序和第三方库的支持如何。
打包前,请务必确保你的源程序可以正常通过 Python 解释器运行。然后执行打包命令,可能会遇到如下错误:
FileNotFoundError: [Errno 2] No such file or directory: 'C:\\...\\Temp\\...\\pgzero\\data\\icon.png'
错误提示缺少一个 icon.png 文件。这是因为 pgzero 库在内部需要引用一个图标资源。解决方法是使用 --include-data-files 参数,将外部文件手动包含到打包产物中。其格式为:
--include-data-files=电脑上资源文件的路径=程序中的调用路径
如果你在 pgzero 模块中找不到这个文件,也可以用任意一张图片替代,只要路径映射正确即可。
对于游戏中用到的 images 图片文件夹和 music 音乐文件夹,则需要使用 --include-data-dir 参数来包含整个目录:
nuitka --standalone --onefile \
--include-data-files=icon.png=pgzero/data/icon.png \
--include-data-dir=images=images \
--include-data-dir=music=music \
sheep.py

配置好所有资源路径后再次打包,等待编译完成,就可以通过生成的独立 exe 文件来运行游戏了。
值得注意的是,由于我们指定了 --standalone 模式,Nuitka 会自动分析和打包程序所依赖的所有模块。另一个优点是,打包后的程序在运行出错时,依然能给出相对清晰的错误提示,这对于调试和问题排查非常关键。
Nuitka 的“隐藏”功能:性能提升
除了打包,Nuitka 还有一个显著的优势:为代码加速。这源于其与众不同的工作原理。
让我们用一个计算密集型的例子来对比。下面是一段包含大量循环和浮点运算的代码:
import time
from typing import List
def complex_computation(n: int) -> List[float]:
start_time = time.time()
"""
一个计算密集型的函数,进行大量数学运算
包含:循环、浮点运算、列表操作等
"""
result = []
for i in range(n):
temp = 0.0
for j in range(n):
temp += (i * j) / (j + 1) ** 2
temp = temp ** 0.5
if j % 2 == 0:
temp = temp * 1.5
else:
temp = temp * 0.7
result.append(temp)
if i % 1000 == 0:
execution_time = time.time() - start_time # 计算执行时间
print(i, f"{execution_time:.2f}")
return result
def main():
n = 10000 # 执行次数
start_time = time.time() # 记录开始时间
result = complex_computation(n) # 执行计算密集型操作
execution_time = time.time() - start_time # 计算执行时间
print(f"执行时间: {execution_time:.2f} 秒")
if __name__ == "__main__":
main()
分别测试直接运行 Python 脚本、使用 PyInstaller 打包后运行、以及使用 Nuitka 打包后运行的耗时,结果对比如下:

可以看到,PyInstaller 打包后的程序运行耗时与直接运行 Python 脚本相近,而 Nuitka 打包后的程序则明显更快。
原理剖析:编译与打包的本质区别
为什么会有这样的性能差异?这就要从两者的根本原理说起了。
像 PyInstaller、cx_Freeze 这类工具,其本质是“打包”。它们将 Python 解释器、你的源代码字节码以及所有依赖库一起,“封装”成一个可执行文件。运行时,依然需要一个内置的解释器来逐行解释执行字节码。

而 Nuitka 的核心是“编译”。它会先将 Python 代码编译成 C 语言代码,然后再调用系统的 C 编译器(如 gcc, MSVC)将 C 代码编译、优化为机器码。因此,Nuitka 的产出物是真正的原生可执行程序,运行时无需解释器参与,执行效率自然更高,尤其适合计算密集型的任务。
跨平台与高级配置
Nuitka 不仅限于 Windows 平台。它支持跨平台编译,例如在 macOS 上可以打包生成 .app 应用程序,此时需要添加 --macos-create-app-bundle 参数,并可用 --macos-app-icon 指定程序图标。
Nuitka 提供了极其丰富的命令行参数,用于精细控制编译和打包行为。通过 nuitka --help 可以查看所有选项。

面对海量的参数,手动配置可能令人望而却步。幸运的是,社区已经开发了图形化辅助工具,例如 NuitkaGUI。这类工具通过可视化的复选框和输入框,让用户可以轻松配置常用参数,大大提升了易用性。

总结与建议
让我们总结一下 Nuitka 的关键特点:
- 独立分发:能将 Python 程序打包成独立的可执行文件,支持包含外部资源和第三方库,兼容主流操作系统。
- 性能优势:其编译原理(Python -> C -> 机器码)使得生成的程序运行效率更高,尤其对 CPU 密集型任务提升明显。
- 高度可配置:提供大量参数满足高级定制需求,配合 GUI 工具可降低使用门槛。

最后需要提醒的是,没有一种打包工具是万能的。不同的项目依赖和代码结构可能导致打包失败或运行时错误。如果你的代码用 Nuitka 打包总是不成功,不妨尝试换用 PyInstaller 等其他工具,或许问题就能迎刃而解。在云栈社区的开发者论坛中,你也可以找到更多关于不同打包工具实战经验的讨论。