page contents

Python exe文件打包神器-Nuitka!

Nuitka 本身是 Python 编译器,会把 Python 模块转换后交给 C 编译器生成二进制程序,同时也能把运行需要的依赖一起处理掉。当前官方稳定版已经到了 4.2.1。

attachments-2026-10-2j6RxCQj6ac8471297f73.png

Python 脚本写完了,自己机器上跑得挺欢。 往别人电脑上一扔:

'python' 不是内部或外部命令

行,没装 Python。

装完 Python 再跑:

ModuleNotFoundError: No module named 'requests'

再补依赖。 折腾一圈,对方已经不想用了。

所以我现在写一些交给测试、运维或者业务同事使用的小工具,只要对方不需要改代码,我一般懒得再发 .py,直接打成 exe。

以前用 PyInstaller 比较多,这两年有些项目我开始换成 Nuitka。

这东西我比较喜欢的一点是:它不是简单把 .py 往包里一塞。

Nuitka 本身是 Python 编译器,会把 Python 模块转换后交给 C 编译器生成二进制程序,同时也能把运行需要的依赖一起处理掉。当前官方稳定版已经到了 4.2.1。

尤其是要给别人发 Windows 工具时,挺顺手。

比如我手上有这么个接口巡检脚本:

from pathlib import Path
from datetime import datetime
import csv

import requests


CHECK_ITEMS = {
    "用户中心": "http://127.0.0.1:8081/health",
    "订单服务": "http://127.0.0.1:8082/health",
    "文件服务": "http://127.0.0.1:8083/health",
}


def ping(name, url):
    started = datetime.now()

    try:
        resp = requests.get(url, timeout=3)
        cost = int((datetime.now() - started).total_seconds() * 1000)

        return [
            name,
            resp.status_code,
            cost,
            "OK" if resp.ok else "BAD_STATUS",
        ]
    except requests.RequestException as exc:
        return [name, "-", "-", exc.__class__.__name__]


def main():
    rows = [ping(name, url) for name, url in CHECK_ITEMS.items()]

    report = Path("check_result.csv")

    with report.open("w", newline="", encoding="utf-8-sig") as fp:
        writer = csv.writer(fp)
        writer.writerow(["服务", "HTTP状态", "耗时(ms)", "结果"])
        writer.writerows(rows)

    print(f"巡检完成:{report.resolve()}")


if __name__ == "__main__":
    main()

平时我自己跑:

python service_check.py

当然没问题。

但我要是把这个脚本丢给现场同事,还顺手补一句:

pip install requests

这种交付我是不太喜欢的。

能让别人双击,就别让别人配 Python 环境。

先装 Nuitka:

python -m pip install -U Nuitka

如果准备使用 onefile 等应用打包能力,也可以直接:

python -m pip install -U "Nuitka[app]"

然后看一眼版本:

python -m nuitka --version

这是官方现在推荐的安装方式。

Windows 第一次编译时,如果机器上没有合适的 C 编译器,Nuitka 可能会询问是否下载 MinGW64 等编译工具。

我一般直接让它装。

除非公司电脑已经统一配好了 MSVC,不然没必要在编译器这里跟自己较劲。官方教程也是这个处理方式。

然后千万别上来就 onefile。

这地方我踩过坑。

很多人看到教程第一条命令就是:

python -m nuitka --onefile service_check.py

确实能打。

但项目一旦有配置文件、图片、模型文件或者某些第三方库,打完发现启动报错,这时候 onefile 排查起来反而麻烦。

我一般先这样:

python -m nuitka --mode=standalone service_check.py

编译完成以后,会生成类似:

service_check.dist/

里面除了 exe,还能看到程序需要的 DLL、Python 扩展和其他依赖。

先进去跑:

service_check.exe

能正常运行,再做下一步。

这个顺序不是我个人洁癖,Nuitka 官方文档本身也建议:先验证 standalone,再尝试 onefile,因为缺文件、缺依赖之类的问题在 standalone 模式下更容易排查。

确认没问题之后,再收成单文件:

python -m nuitka --mode=onefile service_check.py

这时候最终就比较像我们平时理解的“打包 exe”了。

目录里拿到:

service_check.exe

丢到另一台 Windows 机器上运行。

而且现在的 Nuitka 中,--mode=onefile 已经包含 standalone 的行为,不需要再傻乎乎这么写:

python -m nuitka \
    --standalone \
    --onefile \
    service_check.py

直接:

python -m nuitka --mode=onefile service_check.py

够了。

真正容易出问题的,反而不是 Python 文件。

是这些东西:

config.yaml
templates/
images/
sql/
model/
cert/

比如项目目录:

tool/
├─ service_check.py
├─ config/
│  └─ app.yaml
└─ assets/
   └─ logo.png

代码里面直接读:

Path("config/app.yaml").read_text(encoding="utf-8")

开发环境没任何问题。

打完 exe:

FileNotFoundError: config/app.yaml

这种错误我一看基本就不先翻业务代码了。

先看资源有没有进包。

单个文件可以这么带:

python -m nuitka \
    --mode=standalone \
    --include-data-files=config/app.yaml=config/app.yaml \
    service_check.py

整个目录直接:

python -m nuitka \
    --mode=standalone \
    --include-data-dir=assets=assets \
    service_check.py

如果缺的是某个 Python 包自己的数据文件,优先考虑:

--include-package-data=包名

Nuitka 官方也专门提醒了这一点:package data、普通数据文件、DLL 和 Python 模块不是一回事,别为了省事全部当 data file 硬塞。

我之前就见过一种打包命令,后面跟了十几个:

--include-data-files

图片往里塞。

DLL 往里塞。

Python 文件也往里塞。

最后确实打出来了。

也确实跑不起来。

这种参数不是越多越保险,很多时候只是把问题埋得更深。

还有一个很常见的需求:Windows GUI 程序启动时,后面别挂着一个黑乎乎的 CMD 窗口。

比如 Tkinter、PySide6 这种桌面工具。

可以加:

--windows-console-mode=disable

完整一点:

python -m nuitka \
    --mode=onefile \
    --windows-console-mode=disable \
    app.py

当前 Nuitka 对 Windows 控制台还有 force、disable、attach、hide 等模式。普通 GUI 工具,我一般就用 disable。

但是有一个细节。

开发阶段不要急着关控制台。

不然程序启动失败,窗口一闪而过,你连异常是什么都看不到。

我一般调试:

python -m nuitka --mode=standalone app.py

等到 exe 跑稳了,最后发布版本才加:

--windows-console-mode=disable

真遇到 GUI 程序没反应,也先把这个参数删掉再运行。

Nuitka 官方对这种情况也给了相同方向的排查建议:先恢复控制台输出,或者把 stdout、stderr 强制写到文件。

这比盯着一个“点了没反应”的 exe 猜半天强。

exe 图标也顺手一起处理了:

python -m nuitka \
    --mode=onefile \
    --windows-icon-from-ico=assets/tool.ico \
    service_check.py

PNG 现在也可以直接给:

--windows-icon-from-ico=assets/tool.png

Nuitka 会帮忙处理转换。

所以一个普通 Windows 小工具,我最后的命令大概长这样:

python -m nuitka ^
    --mode=onefile ^
    --windows-console-mode=disable ^
    --windows-icon-from-ico=assets/tool.ico ^
    --include-data-dir=config=config ^
    service_check.py

Windows CMD 用的是 ^ 换行。

如果懒得换行:

python -m nuitka --mode=onefile --windows-console-mode=disable --windows-icon-from-ico=assets/tool.ico --include-data-dir=config=config service_check.py

够用了。

别一上来抄网上那种几十个参数的“终极打包命令”。

我看到这种命令第一反应一般不是收藏,是先删。

很多参数根本不是你的项目需要的。

那 Nuitka 和 PyInstaller 到底怎么选?

这个问题没必要站队。

PyInstaller 我现在一样在用。

有个小脚本,只想赶紧扔出去:

Python
  ↓
PyInstaller
  ↓
exe

完全没毛病。

它生态成熟,资料多,碰到奇怪依赖时,网上搜一下通常也容易找到别人踩过的坑。

Nuitka 我会更多放在另外一些东西上:

内部桌面工具
长期分发的小程序
不想直接交付 Python 字节码的项目
对启动、运行效率比较敏感的代码
需要继续维护和发布多个版本的工具

两者路线不太一样。

PyInstaller 更接近“把 Python 程序和运行环境打包起来”。

Nuitka 则是真的会走编译流程,把 Python 代码转换并交给 C 编译器生成二进制程序。Nuitka 官方也把自己明确定位成 Python Compiler,而不仅仅是一个 bundler。

所以有人问:

用了 Nuitka,是不是 Python 源码就绝对安全了?

不是。

这句话别乱吹。

从 .py 直接发出去,变成编译后的原生程序,确实提高了分析门槛,但“编译成 exe”等于“永远无法逆向”,这明显不靠谱。

真正敏感的东西,比如:

API_SECRET = "xxxxxxxx"
DB_PASSWORD = "xxxxxxxx"
PRIVATE_KEY = "xxxxxxxx"

你换什么打包工具都不应该硬编码进去。

exe 不是保险箱。

还有一个坑我觉得比打包参数更值得说。

在哪个平台打,就先按哪个平台的产物理解。

你要 Windows exe,最省事的办法,就是准备 Windows 构建环境。

不要人在 macOS 上,命令敲完以后问:

为什么出来的不是 Windows exe?

Nuitka 最终依赖目标平台的 Python、C 编译器和系统环境。

我自己做正式发布时,一般干脆准备固定的构建机:

Windows 11
指定 Python 版本
指定 requirements.txt
指定 Nuitka 版本

然后打包前:

python -m pip install -r requirements.txt
python -m nuitka --version
python service_check.py

源码先跑。

确认没问题,再编译。

这一点挺重要。

有时候程序本来就跑不起来,结果一打包,报错信息又套了一层,最后所有锅都扣到 Nuitka 头上。

官方教程里其实也专门强调了一句:先确保 Python 程序本身正常运行,再交给 Nuitka 编译。

这个顺序我比较认。

如果只是想把一个 Python 文件变成 Windows exe,记住下面几条其实就够了。

安装:

python -m pip install -U "Nuitka[app]"

先打目录版:

python -m nuitka --mode=standalone app.py

跑通以后再打单文件:

python -m nuitka --mode=onefile app.py

缺自己的资源目录:

--include-data-dir=assets=assets

缺第三方包的数据:

--include-package-data=package_name

GUI 不要黑窗口:

--windows-console-mode=disable

图标:

--windows-icon-from-ico=app.ico

实际项目打包失败时,我的排查顺序基本也是这个:

源码直接运行是否正常
        ↓
standalone 能不能跑
        ↓
有没有缺 DLL / Python 扩展
        ↓
有没有缺配置、模板、图片等数据文件
        ↓
第三方包有没有动态加载
        ↓
最后再折腾 onefile

不是反过来。

尤其看到:

FileNotFoundError
ModuleNotFoundError
DLL load failed

先处理缺的东西。

别第一时间换十套“万能打包参数”。

那玩意儿跟线上 SQL 慢了先重启数据库差不多,偶尔真能好,但你最好别把它当经验。

Python 写工具本身很快,真正麻烦的经常是最后这一步交付。

而 Nuitka 现在给我的感觉就是:编译时间可能多花一点,但只要把 standalone 先跑通,后面做成 exe 交出去,整体还是比较省心。

尤其对那种“我只想让对方双击,别再问我 Python 怎么装”的小工具。

正合适。

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

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

attachments-2022-05-rLS4AIF8628ee5f3b7e12.jpg

 

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

2480 篇文章

作家榜 »

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