你有没有被同事问过一句: “能不能给我一个点两下就能跑的 exe,我电脑没装 Python 啊?”
每次听到这个,心里都在想:哥这是写脚本的,不是发安装包的工程师啊…但没办法,落到我们 Python 搬砖工头上,就是一个字:打包。
今天就聊一个我觉得还挺好用的东西:Nuitka,简单说,就是“把 Python 代码真·编译成 exe 的家伙”。
(顺嘴说一句,之前我写数据库选型的时候顺手测过一些性能场景,这个思路在这次也有点用)
先说一下为啥会注意到 Nuitka
大部分人接触打包,第一反应都是 PyInstaller,对吧。 确实也好用:一条命令出 exe,爽。
但用多了你会发现几个槽点:
很多场景无所谓,比如公司内部工具,大家都习惯了。 但要是给外部用户,或者要放到某些“敏感”环境里,就不太好看。
后来我才知道有这么个 Nuitka,定位就和 PyInstaller 不太一样。
一句话描述: PyInstaller:把 Python 解释器 + 你的代码 + 依赖,打一个大箱子。 Nuitka:把你的 Python 代码,翻译成 C 代码,再用正常 C 编译器编成真正的二进制。
所以,它更像是“编译器”而不是“打包器”。
Nuitka 大概能带来什么好处
别指望它能把屎山一夜之间变成高性能服务,但几个现实的好处还是有的:
真的编译成 native 代码 你的业务逻辑会跑在 C 里,不是单纯丢给解释器去一行行解。 CPU 密集型的地方会舒服一点,尤其是算法、循环比较多的脚本。
发布体验更接近“正经软件” 生成的是一个真正的 exe(或者 Linux 的 ELF),外观上跟 C++ 写的没啥差别, 很多安全软件对这类二进制的“警惕心”反而没那么高。
依赖打包更干净 它走的是 CPython 的 ABI,兼容性也不错,适合你项目里各种第三方轮子一大堆的情况。
一次配置好,后面就当命令行工具用 习惯之后,其实比起手动折腾 PyInstaller 的 spec 文件,要清爽一点。
先来一个最小例子:把 hello.py 编成 exe
先确保你本地 Python 是 3.x,版本不要太老。 然后安装 Nuitka:
pip install -U nuitka
在 Windows 上,还需要有个 C 编译器。 最常见的就是装一个 Visual Studio Build Tools(只勾选 C++ 相关组件就行), 或者装 MinGW-w64 也可以,这块就不展开啰嗦了。
随便写个小脚本 hello.py:
# hello.py
import sys
from datetime import datetime
defmain():
print("Hello, Nuitka!")
print("Python version:", sys.version)
print("Now is:", datetime.now())
if __name__ == "__main__":
main()
用 Nuitka 编译:
python -m nuitka --onefile hello.py
跑完之后,你会在当前目录看到一个 hello.exe(Windows), 双击一下,弹出个控制台窗口,内容跟你在 python hello.py 时一样。
这里几个小点:
python -m nuitka:相当于把 Nuitka 当模块调用--onefile:打成单文件 exe,方便拷来拷去--onefile 的话,它会生成一个目录 + exe,启动更快,但东西多一点稍微像个“工具”的例子:一个小命令行脚本
很多人真实需求是做一个小工具,比如批量改文件名、发 HTTP 请求、查点什么东西。
举个简单点的例子:写个命令行脚本,帮你把一个目录下所有 .txt 文件改成 .bak。
# rename_txt.py
import argparse
import pathlib
defrename_txt_to_bak(folder: str) -> None:
base = pathlib.Path(folder)
ifnot base.exists():
print(f"路径不存在:{base}")
return
count = 0
for p in base.rglob("*.txt"):
new_name = p.with_suffix(".bak")
p.rename(new_name)
count += 1
print(f"处理完成,一共改名 {count} 个文件")
defmain():
parser = argparse.ArgumentParser(description="批量把 .txt 改成 .bak 的小工具")
parser.add_argument("folder", help="目标目录路径")
args = parser.parse_args()
rename_txt_to_bak(args.folder)
if __name__ == "__main__":
main()
打包命令也很简单:
python -m nuitka --onefile rename_txt.py
生成的 rename_txt.exe 丢给测试同事,让他在自己的 Windows 上打开 cmd 这样用:
rename_txt.exe D:\logs
他完全不用关心什么 Python 解释器、虚拟环境,照样跑。
再来个带三方库的例子:requests + 简单重试
很多业务脚本会发 HTTP 请求,我们就做个小一点的“接口探活工具”。
# ping_url.py
import argparse
import time
import requests
defping(url: str, attempts: int = 3, interval: float = 1.0) -> None:
for i in range(1, attempts + 1):
try:
resp = requests.get(url, timeout=3)
print(f"[{i}] {url} -> {resp.status_code}")
except requests.RequestException as exc:
print(f"[{i}] {url} 请求失败:{exc}")
time.sleep(interval)
defmain():
parser = argparse.ArgumentParser(description="简单的 HTTP 探活小工具")
parser.add_argument("url", help="目标 URL")
parser.add_argument("-n", "--attempts", type=int, default=3, help="重试次数")
parser.add_argument("-i", "--interval", type=float, default=1.0, help="重试间隔秒数")
args = parser.parse_args()
ping(args.url, args.attempts, args.interval)
if __name__ == "__main__":
main()
因为用到了 requests,强烈建议先建个虚拟环境,只装项目需要的依赖:
python -m venv venv
venv\Scripts\activate # Windows
pip install -U pip
pip install requests nuitka
然后在虚拟环境里编译:
python -m nuitka --onefile ping_url.py
这样打出来的 exe 里只会带上你这个环境里的包,体积和冗余都会好一点。
常用几个 Nuitka 参数,大概知道一下就够
别被一大片参数吓到,最常用的就那几个:
--onefile单文件打包,适合发给别人。换成 --standalone 就是目录模式。
--windows-disable-consoleWindows 下如果你做的是 GUI 程序,不想弹黑框,就加这个。 比如你用 Tkinter / PyQt 做了个小工具:
python -m nuitka --onefile --windows-disable-console main_gui.py
--include-data-dir / --include-data-file有时候你要带上模板文件、图片之类的静态资源,可以这样:
python -m nuitka --onefile ^
--include-data-dir=assets=assets ^
main_gui.py
这个写法的意思是:把工程里的 assets 目录原样打包进去, 运行时也保持 assets 这个路径。
--enable-plugin=比如你用的是 PySide6、PyQt5 这种 GUI 库, Nuitka 自己搞了一堆 plugin,帮你处理各种奇怪的导入方式:
python -m nuitka --onefile ^
--enable-plugin=pyside6 ^
main_gui.py
一般来说,遇到框架级别的库(Django / Flask / PySide / PyQt 之类), 在官方文档里搜一下 “Nuitka plugin xxx” 基本都有对应示例。
和 PyInstaller 比到底差在哪、强在哪
日常使用感受,粗暴对比一下:
速度方面: CPU 密集的脚本,Nuitka 编译后基本都能比纯解释执行快一些, I/O 密集和网络调用那种,就别指望有奇迹了,网络延迟谁也改变不了。
打包体验: PyInstaller 上手快,遇到各种“找不到模块”就开始加隐藏导入、调 spec; Nuitka 初次配置麻烦一点(特别是编译器那块), 但后面参数稳定下来,就像正常编译 C 项目一样重复使用。
体积: 这俩其实都不算小,看项目依赖。 Nuitka 有时候会稍微好看一点,但也别指望一个几十行脚本打完只有几百 K。
适用场景:
几个容易踩的小坑,提前心里有数
随便提醒几个常见问题,免得你调一下午:
“为啥我在自己电脑能跑,打完给别人的电脑提示找不到 xxx.dll?”
“exe 太慢了,是不是我用错参数了?”
--onefile,先用目录模式 --standalone 看下启动速度。“杀毒软件说我可疑程序,咋办?”
“编译太慢了,每次改几行都要等半天”
.py,确认逻辑没问题再编译。什么时候值得你认真学一下 Nuitka
如果你平时就写一些一次性的脚本,其实完全没必要专门学它,PyInstaller 甚至 zip 打包扔给会用 Python 的同事就够了。
但下面几种情况,我觉得还是挺值得折腾一下的:
Nuitka 不是什么银弹,但它确实把“Python 只能当脚本语言”的那层壳,掀开了一点点。 你会发现:原来 Python 也可以踏踏实实编成一个像样的程序,对运维、对测试、对你的甲方,都友好一些。
行,差不多就聊到这儿。 你要是正好手上有个 Python 小工具迟迟懒得打包,不如现在随手装个 Nuitka,挑个脚本试一把,踩踩坑比啥都实际。
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html