
PyInstaller vs Nuitka 终极瘦身实测Matplotlib/Numpy 项目体积优化全记录每次用 Python 写完工具准备分享给同事时最尴尬的莫过于对方看到几百兆的安装包后那句这真是个小工具吗。上周我用 Matplotlib 做了个简单的数据可视化工具PyInstaller 打包后居然有 42MB而同样的功能用 C 编译出来可能不到 5MB。这促使我系统性地测试了主流 Python 打包方案及其瘦身技巧特别是当项目包含科学计算库时的优化空间。1. 测试环境与基准建立1.1 实验设计思路为了获得可比性数据我设计了一个标准测试用例一个使用 Numpy 生成随机数据并用 Matplotlib 绘制散点图的脚本。选择这两个库是因为它们是数据科学项目的典型依赖包含大量可能未使用的编译文件和资源在不同打包工具中的处理方式差异明显测试环境配置如下环境项配置详情操作系统Windows 11 22H2Python 版本3.9.13 (Miniconda 环境)测试库版本Numpy 1.23.5, Matplotlib 3.6.2打包工具版本PyInstaller 5.7.0, Nuitka 1.4.61.2 基准代码实现# scatter_plot.py import numpy as np import matplotlib.pyplot as plt def generate_plot(): np.random.seed(2023) x np.random.normal(0, 1, 500) y np.random.normal(0, 1, 500) plt.style.use(ggplot) fig, ax plt.subplots(figsize(8,6)) ax.scatter(x, y, alpha0.6, cnp.sqrt(x**2 y**2), cmapviridis) ax.set_title(Multivariate Normal Distribution, pad20) ax.set_xlabel(X Value, labelpad10) ax.set_ylabel(Y Value, labelpad10) plt.savefig(scatter.png, dpi120, bbox_inchestight) plt.close() if __name__ __main__: generate_plot() input(Plot generated. Press Enter to exit...)这段代码特意包含了 Matplotlib 的样式设置和复杂绘图选项以确保打包时包含完整的库功能。2. 初始打包体积对比2.1 PyInstaller 打包结果使用以下命令进行打包pyinstaller --onefile --clean --upx-dir./upx-3.96-win64 scatter_plot.py打包结果分析文件类型大小 (MB)占比主要构成原始 EXE42.7100%包含所有依赖的独立可执行文件解压后文件夹158.2370%运行时临时解压的全部内容关键子目录├─matplotlib89.356.4%字体、样式、后端驱动等├─numpy37.823.9%核心库及扩展模块└─PIL12.17.6%图像处理相关依赖2.2 Nuitka 打包结果使用以下命令进行打包nuitka --onefile --remove-output --plugin-enablenumpy --windows-icon-from-icoicon.ico scatter_plot.py打包结果对比指标PyInstallerNuitka差异EXE 文件大小42.7MB38.2MB-10.5%启动时间2.3s1.1s-52.2%内存占用145MB112MB-22.8%依赖文件数量327个291个-11.0%Nuitka 的打包优势主要来自更智能的依赖分析C 编译优化动态链接部分系统库3. 瘦身工具实战应用3.1 瘦身工具工作原理我们使用的瘦身工具基于动态分析原理其工作流程如下运行监控阶段启动待分析的可执行文件使用 API Hook 技术记录所有文件访问操作依赖分析阶段生成实际使用的文件清单对比打包目录中的全部文件清理优化阶段移动未使用的文件到备份目录生成文件迁移报告保留原始目录结构3.2 PyInstaller 瘦身过程操作步骤python shrink_tool.py --targetdist/scatter_plot --log-leveldebug关键日志输出[DEBUG] 开始分析目录: dist/scatter_plot [INFO] 检测到 PyInstaller 打包结构 [DEBUG] 监控到已加载模块: numpy.core._multiarray_umath [DEBUG] 监控到资源访问: matplotlib/mpl-data/fonts/ttf/DejaVuSans.ttf [WARNING] 未使用文件: matplotlib/backends/_backend_tk.cp39-win_amd64.pyd瘦身前后对比项目瘦身前瘦身后缩减比例总文件数32718443.7%文件夹大小158.2MB67.5MB57.3%运行时内存145MB138MB4.8%3.3 Nuitka 瘦身效果针对 Nuitka 的特殊处理# Nuitka 专用白名单配置 { nuitka_specific: [ *.dist/Microsoft.VC140.CRT/*, *.dist/python39.dll, *.dist/zlib.pyd ], always_keep: [ **/site-packages/numpy/core/*.dll, **/matplotlib/backends/backend_qt5agg.py ] }优化结果优化阶段文件大小较原始缩减初始打包38.2MB-基础瘦身29.7MB22.3%手动优化后24.1MB36.9%UPX 压缩后18.4MB51.8%4. 深度优化技巧4.1 依赖树修剪技术通过pipdeptree分析发现可以优化的依赖pipdeptree --packages numpy,matplotlib --freeze | grep -v ^\s输出显示可以移除的间接依赖mkl-service2.4.0 pyparsing3.0.9 cycler0.11.0优化方法# setup.cfg 中添加排除项 [options] install_requires numpy1.21 matplotlib3.5 exclude_packages mkl pyparsing4.2 资源文件选择性打包Matplotlib 字体优化方案创建自定义字体清单# font_list.py from matplotlib import font_manager fonts [f.name for f in font_manager.fontManager.ttflist if f.name in (DejaVu Sans, Arial)] print(fRequired fonts: {fonts})修改打包配置# hook-matplotlib.py datas [ (mpl-data/fonts/ttf/DejaVuSans.ttf, matplotlib/mpl-data/fonts/ttf), (mpl-data/matplotlibrc, matplotlib/mpl-data) ]4.3 二进制文件压缩对比测试不同压缩工具效果工具压缩级别PyInstaller 大小Nuitka 大小耗时无压缩-42.7MB38.2MB-UPX--best31.5MB (-26.2%)28.1MB (-26.4%)45sAspackMax29.8MB (-30.2%)26.4MB (-30.9%)68sMPRESSUltra28.3MB (-33.7%)25.1MB (-34.3%)52s注意过度压缩可能导致杀毒软件误报建议测试后选择平衡方案5. 工程实践建议经过两周的测试和十几次打包迭代总结出以下实用经验工具选型策略快速原型开发 → PyInstaller调试方便生产环境发布 → Nuitka性能更优极致体积需求 → PyInstaller 深度瘦身目录结构优化dist/ ├── app.exe # 主程序 ├── runtime/ # 动态加载的依赖 │ ├── numpy/ # 科学计算库 │ └── matplotlib/ # 绘图相关 └── data/ # 资源文件持续集成配置# .github/workflows/build.yml - name: Build with Nuitka run: | nuitka --onefile --include-package-datamatplotlib \ --remove-output --output-dirdist src/app.py python shrink_tool.py --targetdist/app.dist \ --config.shrinkrc.json upx --lzma --best dist/app.exe