3、PyInstaller基础:安装与基本命令、单文件打包、单目录打包、常用参数详解
说到Python打包,我第一个想到的工具就是PyInstaller。说实话,市面上打包工具不少,像cx_Freeze、py2exe这些我都用过,但最后一直留在项目里的,还是PyInstaller。为什么?因为它省心。你写好的Python脚本,不管用了什么第三方库,它基本都能帮你收拾干净,打包成一个可执行文件。
嗯,咱们直接进入正题。这一章我会把PyInstaller的安装、基本命令、两种打包模式,还有那些你早晚会用到的参数,一次性讲清楚。
3.1 安装PyInstaller
安装没什么花头,一行命令的事。我个人习惯用pip,干净利落:
pip install pyinstaller
如果你像我一样,经常在多个项目间切换,建议装到虚拟环境里。我在项目中遇到过好几次,因为全局安装导致版本冲突,折腾了半天才发现是依赖打架了。
装完之后,验证一下:
pyinstaller --version
能看到版本号,说明装好了。目前稳定版是5.x,我建议你用最新版,因为旧版对Python 3.11+的支持不太好。
3.2 基本命令结构
PyInstaller的命令结构其实很简单,说白了就是:
pyinstaller [参数] 你的脚本.py
你想想看,核心就三要素:命令、参数、脚本文件。参数决定了打包方式,脚本文件就是你要打包的那个入口文件。
我第一次用的时候,直接敲了pyinstaller myapp.py,结果生出来一堆文件夹,差点没把我整懵。后来才明白,默认行为是生成单目录打包。那咱们就从这个开始讲起。
3.3 单目录打包
单目录打包,也叫目录模式。执行这个命令:
pyinstaller myapp.py
你会得到一个dist文件夹,里面有个myapp子目录。可执行文件就在这个目录里,旁边还跟着一堆依赖的dll和pyd文件。
这种模式的好处是:
- 启动速度快,因为依赖文件都在外面
- 调试方便,缺什么文件一目了然
- 更新时只需要替换主程序文件
缺点也很明显——你给别人发程序,总不能发一个文件夹吧?我有个朋友,把整个目录压缩成zip发过去,对方解压后双击没反应,结果发现是路径里有中文乱码了。嗯,这里要注意,中文路径有时候会坑你。
3.4 单文件打包
单文件打包,就是生成一个独立的exe文件。命令是:
pyinstaller -F myapp.py
或者用全称:
pyinstaller --onefile myapp.py
打包完成后,dist目录里只有一个myapp.exe。双击运行,它会先自解压到一个临时目录,然后再启动你的程序。
为什么会这样?因为单文件本质上是一个自解压包。程序启动时,会把所有依赖文件解压到系统临时目录里,然后才运行。所以你会发现,单文件启动比单目录慢那么一两秒。
我在项目中遇到过一个问题:用单文件打包后,程序里读取资源文件的路径全乱了。后来才明白,单文件模式下,当前工作目录和可执行文件所在目录不是一回事。解决方案是用sys._MEIPASS来获取解压后的临时路径。
3.5 常用参数详解
参数这东西,你记不住没关系,但常用的那几个必须得知道。我整理了一张表,你直接收藏就行:
| 参数 | 全称 | 作用 | 我的经验 |
|---|---|---|---|
-F |
--onefile |
打包成单个exe文件 | 适合分发给用户,但启动稍慢 |
-D |
--onedir |
打包成单目录(默认) | 调试时首选,方便查看依赖 |
-w |
--windowed |
不显示控制台窗口 | GUI程序必加,不然会多个黑框 |
-c |
--console |
显示控制台窗口(默认) | 命令行工具用这个 |
-i |
--icon |
指定程序图标 | 支持ico、png,建议用256x256 |
--hidden-import |
— | 手动指定隐式导入的模块 | 动态导入的库必须手动加 |
--add-data |
— | 添加额外数据文件 | 配置文件、图片资源用这个 |
--clean |
— | 清理缓存后打包 | 遇到奇怪问题先试试这个 |
--noconfirm |
— | 覆盖输出目录时不询问 | 自动化脚本里必加 |
来,看个实际例子。假设我要打包一个带GUI的图片处理工具:
pyinstaller -F -w -i app.ico --add-data "config.json;." --hidden-import PIL.ImageFilter image_tool.py
这条命令干了这些事:
-F:打包成单个exe-w:隐藏控制台窗口-i app.ico:设置图标--add-data "config.json;.":把配置文件打包进去--hidden-import PIL.ImageFilter:手动引入PIL的滤镜模块
你可能会问,为什么要手动加--hidden-import?因为PyInstaller是通过静态分析来发现依赖的,有些模块是运行时才动态导入的,它检测不到。我曾经有个项目用了importlib动态加载插件,结果打包出来功能不全,排查了半天才发现是少了几个隐式导入。
3.6 一个完整的打包示例
咱们来个完整的流程,你跟着走一遍:
# 1. 安装
pip install pyinstaller
# 2. 单目录打包(调试用)
pyinstaller myapp.py
# 3. 单文件打包(发布用)
pyinstaller -F -w -i myicon.ico myapp.py
# 4. 清理缓存
pyinstaller --clean myapp.py
打包完成后,你会看到项目目录里多了两个文件夹:build和dist。build是临时文件,可以删掉。dist里才是你要的东西。
--clean,避免缓存导致的问题。另外,dist目录我会用版本号命名,比如dist_v1.2,方便回退。
3.7 避坑指南
最后,分享几个我踩过的坑:
- 路径问题: 程序里用相对路径时,单文件模式下会找不到文件。解决方案是用
os.path.dirname(sys.executable)获取实际路径。 - 大文件打包: 我曾经把一个100MB的模型文件打包进去,结果exe体积直接飙到200MB,启动还巨慢。后来改成运行时下载,体验好多了。
- 杀软误报: 单文件exe容易被报毒。可以试试加壳或者数字签名,或者改用单目录模式分发。
- Python版本: 32位Python打包出来的exe是32位的,64位的是64位的。如果你要兼容旧系统,记得用32位Python打包。
嗯,这一章的内容就到这。PyInstaller的基础操作其实不难,关键是理解两种打包模式的区别,以及那几个常用参数怎么用。下一章咱们会深入聊聊spec文件,那才是真正定制化打包的核心。