3、PyInstaller基础:安装与基本命令、单文件打包、单目录打包、常用参数详解

说到Python打包,我第一个想到的工具就是PyInstaller。说实话,市面上打包工具不少,像cx_Freeze、py2exe这些我都用过,但最后一直留在项目里的,还是PyInstaller。为什么?因为它省心。你写好的Python脚本,不管用了什么第三方库,它基本都能帮你收拾干净,打包成一个可执行文件。

嗯,咱们直接进入正题。这一章我会把PyInstaller的安装、基本命令、两种打包模式,还有那些你早晚会用到的参数,一次性讲清楚。

3.1 安装PyInstaller

安装没什么花头,一行命令的事。我个人习惯用pip,干净利落:

pip install pyinstaller

如果你像我一样,经常在多个项目间切换,建议装到虚拟环境里。我在项目中遇到过好几次,因为全局安装导致版本冲突,折腾了半天才发现是依赖打架了。

装完之后,验证一下:

pyinstaller --version

能看到版本号,说明装好了。目前稳定版是5.x,我建议你用最新版,因为旧版对Python 3.11+的支持不太好。

小提示: 如果你用的是Python 3.12或更高版本,记得检查PyInstaller的兼容性。我曾经因为升级了Python,结果打包出来的exe一运行就闪退,查了半天才发现是版本不匹配。

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来获取解压后的临时路径。

警告: 单文件打包后,杀毒软件容易误报。我有个工具被360报毒,用户直接给删了。后来加了数字签名才解决。如果你要分发到外部,建议先做签名。

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文件,那才是真正定制化打包的核心。