用 Python 写完一个小工具以后,经常会遇到下一个问题:怎么把它交给别人用?
对方的电脑可能没有 Python,可能缺少依赖,也可能不想为了运行一个小脚本折腾半天环境。有时候我也不想直接把源码发出去,于是最省事的办法通常就是打包成 EXE。
当然,Python 打包早就不是什么新鲜事了。对大多数项目来说,PyInstaller 已经足够好用,一行命令就能解决:
pyinstaller -F main.py
但我在实际使用时一直有个烦恼。
我平时习惯直接使用全局 Python 环境,里面装了很多科研、绘图和开发用的库。PyInstaller 并不会无脑把环境里的所有库都塞进 EXE,但环境越复杂,越容易被额外的依赖、资源文件和打包钩子影响,最后生成的文件更大,打包结果也更难复现。
更稳妥的办法,是给项目新建一个干净的虚拟环境,只安装项目真正需要的依赖,再装一个 PyInstaller,然后在这个环境里打包。
但每次都这么做实在有点烦。
一个稍微大一点的项目还好,如果只是临时写了个小脚本,也要新建 venv、激活环境、重新安装依赖、执行打包,最后再清理环境。打包本身可能只需要一分钟,前面的准备工作反而更让人不想开始。
于是我去问了 GPT:有没有办法把这套流程再简化一点?
最后采用的方案是 uv + PyInstaller。
简单来说,就是先从入口脚本出发扫描实际导入的模块,生成运行依赖;再让 uv 准备一个干净的临时环境,只安装这些依赖和 PyInstaller;最后在这个隔离环境里完成打包。
它不会直接使用我当前的开发虚拟环境,也不会在项目目录里塞一个 .venv。下载过的 Python 包会进入统一缓存,以后遇到相同版本可以继续复用,不需要每个项目都从头下载一遍。
然后我顺便让 GPT 把整套流程做成了一个小工具。
使用时只需要选择项目目录和入口文件,设置程序名称、输出形式、窗口类型等选项,然后点击“开始打包”。
依赖也不需要自己一项一项整理。工具会从入口文件开始,递归跟踪实际导入的本地模块,再把识别到的第三方包写进 runtime-requirements.txt。
当然,依赖扫描不可能百分之百准确。插件系统、字符串形式导入和运行时动态加载的模块,仍然可能需要手动添加“隐藏导入”;而且 import 名也不一定等于 pip 包名,例如 PIL 对应的是 Pillow。
所以自动扫描解决的是大部分重复劳动,打包完成后该测试还是得测试。
顺手解决了 EXE 图标
打包还有一个很小、但每次都会遇到的问题:图标。
不设置图标,所有 EXE 看起来都差不多;想设置图标,又要找图片、裁尺寸、转换成 .ico。为了一个临时小工具专门手搓图标,多少有点劝退。
所以我又让 GPT 加了一个“表情图标”功能。
点击“插入表情”,直接从面板里选一个 emoji,工具会自动把它转换成包含多种尺寸的 Windows 图标,再交给 PyInstaller 使用。没有收录的表情,也可以从外部复制后粘贴进输入框。
这样做当然比不上专门设计的图标,但对于自己写的小工具已经足够了。至少不同程序放在桌面上时,不会再排成一排一模一样的默认图标。
不过由于我用的版本里tkinter是无法显示彩色表情的,因此表情面板都是黑白,但是打包出来以后是正常的彩色。
用打包工具打包它自己
工具做好以后,我干脆用它打包了一次它自己。
项目目录、入口文件、依赖文件和输出名称填好以后,点击“开始打包”。工具会调用 uv 创建隔离环境,再执行 PyInstaller。整个过程的命令和日志都会实时显示在界面里,出错时不用去猜卡在了哪一步。
这次生成的独立版 EXE 大约 19 MB。它把 Python 解释器和运行依赖一起带上,目标电脑即使没有安装 Python,也可以直接运行。
对于正式分享给别人使用的工具,我还是更倾向于这种方式。体积大一点,但省去了环境配置,出问题的概率也更低。
我还做了一个 100 KB 左右的精简版
后来我又想到,现在很多人的电脑其实已经有 Python 环境了。如果只是自己在几台电脑之间传一个小工具,每次都把解释器和依赖一起打进去,十几 MB 起步,似乎还是有点浪费。
于是我又让 GPT 增加了“系统 Python 启动器”模式。
这个模式生成的 EXE 不内置 Python,也不内置第三方依赖。运行时,它会直接调用目标电脑 PATH 中的 python.exe 来执行内置源码。
同一个项目实测下来,独立版是 19,082 KB,精简启动器只有 104 KB。
代价也很明显:目标电脑必须已经安装 Python,而且脚本需要的第三方库也必须存在。缺少依赖、Python 版本不兼容,或者系统 PATH 配置有问题,都可能导致运行失败。
所以它更适合自己使用,或者用在环境已经统一的电脑上。我目前用得还不算多,这个模式可能仍然有一些没有发现的 BUG。
如果要把工具发给完全不了解 Python 的人,还是老老实实选择独立版更稳。
最后
这个工具本身没有发明新的打包技术。底层仍然是 uv、PyInstaller 和系统 Python,只是把我每次都嫌麻烦的步骤串了起来:
选择项目、扫描依赖、准备隔离环境、生成图标、执行打包、查看日志、打开输出目录。
以前这些步骤我也会做,只是不愿意每次都做。
现在它们变成了几个选项和一个“开始打包”按钮。
我越来越觉得,vibe coding 最适合解决的,不一定是什么宏大的需求。很多时候,只是把自己反复做、每次都嫌麻烦的一套操作,变成一个下次还能继续用的小工具。
能少折腾一次环境,也算没有白折腾。
后台回复"python打包"关键词获取本工具。本号将持续发布个人效率小工具、skill等,关注获取更多信息。