Python 这门语言写起来是真舒服但一到交付环节很多人就卡住了——脚本在自己电脑上跑得好好的发给同事或者客户对方一句我没装 Python就把你堵回来了。这时候把程序打包成 EXE 或者 APK就成了绕不过去的一步。我自己这些年打包过不少东西从最简单的命令行小工具到带图形界面的桌面软件再到用 Kivy 写的安卓应用踩过的坑能写满一页纸。这篇就把 Python 打包成 EXE 和 APK 的完整流程、工具选型、参数配置、以及那些文档里不会写的坑一次性讲清楚。不管你是刚入门想把自己写的小工具分享出去还是已经有一定基础想把项目正式交付都能从里面找到能直接抄作业的东西。1. 打包之前先想清楚你到底要交付什么很多人一上来就问用什么工具打包其实这个问题问早了。打包工具的选择完全取决于你要交付的形态和运行环境。先把这个想明白后面能少走一大半弯路。1.1 EXE 和 APK 是两种完全不同的交付目标EXE 是 Windows 平台的可执行文件用户双击就能运行不需要安装 Python 解释器。它的本质是把 Python 解释器、你的代码、以及所有依赖库一起塞进一个文件或者一个文件夹里运行时由打包工具生成的引导程序负责启动解释器并加载你的代码。APK 是 Android 平台的安装包用户安装到手机上运行。它和 EXE 最大的区别在于Android 系统根本不认识 Python所以你需要一个能把 Python 代码跑在安卓上的运行时环境。目前主流方案是 Kivy 配合 Buildozer或者 BeeWare 配合 Briefcase。这两条路的技术栈、构建环境、调试方式都完全不同。所以第一步先回答自己三个问题目标用户用的是 Windows 还是 Android程序有没有图形界面还是纯命令行有没有用到那些重依赖比如 OpenCV、PaddleOCR、PyQt、TensorFlow这三个问题的答案直接决定了你后面用哪个工具、在什么系统上构建、以及会遇到哪类坑。1.2 工具选型PyInstaller、cx_Freeze、Nuitka 怎么选Windows 平台打包 EXE主流工具有三个我列个表对比一下实际使用感受工具打包速度生成体积反编译难度适用场景PyInstaller中等偏大容易通用首选兼容性最好cx_Freeze较快中等容易需要精细控制文件结构Nuitka慢中等较难对性能或代码保护有要求PyInstaller 是我用得最多的原因很简单它对第三方库的兼容性最好绝大多数库开箱即用社区资料也最全。cx_Freeze 的优势在于配置文件更灵活适合需要精确控制打包内容的场景。Nuitka 则是把 Python 代码编译成 C 再编译成机器码运行速度有提升反编译难度也更高但打包过程慢而且对某些动态导入的库支持不够好。我的建议是除非你有明确的性能或代码保护需求否则直接用 PyInstaller。它能把 90% 的场景搞定剩下的 10% 再考虑换工具。1.3 一个容易被忽略的前置问题虚拟环境这一点我必须单独拎出来说因为它是我早期踩过最大的坑。如果你在全局环境里打包PyInstaller 会去分析你整个 Python 环境的依赖结果就是打出来的 EXE 体积巨大还可能把一些根本用不到的库也塞进去甚至因为某些库的冲突导致打包失败。正确做法是为每个项目单独建一个虚拟环境只装这个项目真正需要的依赖。# 创建虚拟环境 python -m venv venv # Windows 激活 venv\Scripts\activate # macOS/Linux 激活 source venv/bin/activate # 在虚拟环境里安装依赖 pip install pyinstaller pip install 你的项目依赖这样打包出来的体积会小很多而且能避免很多莫名其妙的依赖冲突。我有个项目在全局环境打包出来 300 多 MB换成干净的虚拟环境后只有 40 多 MB差距就是这么夸张。2. PyInstaller 打包 EXE从命令到参数逐个拆解PyInstaller 的命令行参数不少但真正需要天天用的就那么几个。这一节我把核心参数、打包模式、以及实际项目里的配置方式讲透。2.1 两种打包模式单文件和单目录PyInstaller 有两种打包模式这是新手最容易纠结的地方。单文件模式-F 或 --onefile所有东西打包成一个 EXE 文件。优点是干净发给别人就一个文件。缺点是每次运行都要先把内容解压到临时目录启动速度慢尤其是体积大的程序第一次启动可能要等好几秒。单目录模式-D 或 --onedir默认生成一个文件夹里面包含 EXE 和一堆依赖文件。优点是启动快缺点是文件多发给别人要打包整个文件夹。# 单文件模式 pyinstaller -F main.py # 单目录模式 pyinstaller -D main.py我的实际经验是小工具用单文件大程序用单目录。如果你的程序启动时间超过 3 秒用户会明显感觉到卡顿这时候单目录更合适。如果只是个小脚本单文件的便利性更重要。2.2 那些必须知道的参数光会用 -F 和 -D 是不够的下面这些参数在实际项目里几乎都会用到pyinstaller -F -w -i icon.ico --name MyApp main.py逐个解释-w或--windowed不显示控制台窗口。GUI 程序必须加这个否则会弹出一个黑框。但注意如果你的程序有 print 调试信息加了 -w 就看不到了调试阶段先别加。-i icon.ico指定程序图标。图标必须是 .ico 格式而且建议包含多种尺寸16x16、32x32、48x48、256x256否则在某些地方显示会模糊。--name MyApp指定生成的 EXE 名称默认是脚本文件名。--add-data打包额外的数据文件比如图片、配置文件、模型文件。这个参数在不同系统上分隔符不一样Windows 用分号Linux/macOS 用冒号。# Windows pyinstaller -F --add-data data;data main.py # Linux/macOS pyinstaller -F --add-data data:data main.py--hidden-import手动指定那些 PyInstaller 分析不出来的隐藏导入。有些库用动态导入的方式加载模块PyInstaller 静态分析发现不了运行时就报 ModuleNotFoundError。这时候就要手动加。--exclude-module排除不需要的模块减小体积。比如你的程序用不到 tkinter就可以排除掉。2.3 用 spec 文件管理复杂配置当参数多起来之后命令行会变得很长很难维护。这时候就该用 spec 文件了。第一次运行 pyinstaller 命令后会在目录下生成一个 .spec 文件它其实就是个 Python 脚本描述了打包的配置。# main.spec 示例 a Analysis( [main.py], pathex[], binaries[], datas[(data/config.json, data)], # 添加数据文件 hiddenimports[pkg_resources.py2_warn], # 隐藏导入 hookspath[], excludes[tkinter, matplotlib], # 排除模块 ... )改完 spec 文件后用pyinstaller main.spec来打包。这样配置就固化下来了团队协作或者换电脑都能复用。我个人的习惯是项目稳定后一定把 spec 文件纳入版本管理因为它记录了所有打包相关的配置比命令行可靠得多。2.4 资源路径问题打包后文件找不到的根因这是 PyInstaller 打包后最高频的问题没有之一。你的代码里写了open(config.json)开发时好好的打包后一运行就报 FileNotFoundError。根本原因在于单文件模式下程序运行时会把内容解压到一个临时目录sys._MEIPASS而你的代码用的是相对路径工作目录变了自然找不到文件。解决方案是写一个统一的资源路径函数import sys import os def resource_path(relative_path): 获取资源的绝对路径兼容开发环境和 PyInstaller 打包后 if hasattr(sys, _MEIPASS): # PyInstaller 打包后的临时目录 base_path sys._MEIPASS else: # 正常开发环境 base_path os.path.abspath(.) return os.path.join(base_path, relative_path) # 使用 config_path resource_path(data/config.json)这个函数我几乎每个打包项目都会放进去一劳永逸。记住一点所有读取打包资源的路径都要走这个函数写文件的路径则要用另外的方式处理因为临时目录在程序退出后会被清理。3. 打包体积和启动速度能优化的地方比你想的多打包出来的 EXE 动辄几十上百 MB启动还慢这是 PyInstaller 被吐槽最多的地方。但其实有很多优化空间只是大部分人不知道。3.1 体积从哪来拆解一个 EXE 的构成一个打包后的 EXE体积主要来自这几块Python 解释器本身约 5-10 MB标准库看你用到多少通常 10-20 MB第三方库这是大头numpy 约 30 MBpandas 约 40 MBPyQt5 约 60 MB你的代码通常很小所以体积优化的核心思路就是把用不到的第三方库和标准库模块排除掉。3.2 用 exclude 和虚拟环境双管齐下前面提到的虚拟环境是第一道防线它保证你只装了必要的库。第二道防线是 exclude 参数pyinstaller -F --exclude-module tkinter --exclude-module matplotlib --exclude-module scipy main.py但要注意exclude 要谨慎用。有些库是间接依赖你排除了它程序运行时才报错而且报错信息往往很隐晦。我的做法是先打包一个完整版跑通所有功能再逐个排除每排除一个就完整测试一遍。3.3 UPX 压缩用之前先想清楚PyInstaller 支持用 UPX 来压缩二进制文件能显著减小体积。安装 UPX 后PyInstaller 会自动调用它。# 安装 UPX 后打包时加 --upx-dir 指定路径 pyinstaller -F --upx-dir/path/to/upx main.py但 UPX 有个坑某些杀毒软件会把 UPX 压缩过的 EXE 误报为病毒。这是因为 UPX 的压缩特征和某些恶意软件相似。如果你的程序要发给别人用尤其是企业环境我建议慎用 UPX或者先做一下杀毒软件兼容性测试。3.4 启动速度优化单文件模式的代价单文件模式启动慢是因为每次运行都要解压。如果你实在需要单文件又想快一点可以试试这些方法减少打包内容体积小了解压自然快用--runtime-tmpdir指定一个固定的临时目录避免每次换目录考虑改用单目录模式用安装包工具如 Inno Setup打包成安装程序说实话如果启动速度是硬指标单目录模式加安装包是更专业的方案。单文件模式适合那些对启动速度不敏感的小工具。4. Kivy Buildozer 打包 APK一条需要耐心的路把 Python 打包成 APK比打包 EXE 复杂得多。因为 Android 是个完全不同的生态你需要交叉编译环境、Android SDK、NDK 等等。目前最成熟的方案是 Kivy Buildozer但它的构建过程对新手不太友好尤其是在 Windows 上。4.1 为什么推荐在 Linux 上构建 APKBuildozer 官方明确建议在 Linux 环境下构建Windows 上虽然可以通过 WSL 或者虚拟机来跑但坑会多很多。原因在于 Buildozer 依赖大量的 Linux 命令行工具和 Python 构建工具链在 Windows 原生环境下很多都跑不通。我的建议是直接用一台 Linux 机器或者在 Windows 上装 WSL2用 Ubuntu 环境来构建。这样能避开 90% 的环境问题。4.2 环境准备一次装对省下半天在 Ubuntu 环境下先装好基础依赖sudo apt update sudo apt install -y python3-pip python3-venv git zip unzip openjdk-17-jdk \ autoconf libtool pkg-config zlib1g-dev libncurses5-dev libncursesw5-dev \ libtinfo5 cmake libffi-dev libssl-dev然后建虚拟环境装 Buildozer 和 Cythonpython3 -m venv venv source venv/bin/activate pip install buildozer cython这里有个细节Cython 的版本很关键。Buildozer 对 Cython 版本有要求太新或太旧都可能导致构建失败。如果遇到奇怪的编译错误先试试降级 Cython。4.3 buildozer.spec 配置详解在项目目录下运行buildozer init会生成一个 buildozer.spec 文件。这个文件是 APK 构建的核心配置几个关键项必须改对[app] # 应用名称 title MyApp # 包名必须是反向域名格式 package.name myapp # 域名 package.domain org.example # 入口文件 source.dir . source.include_exts py,png,jpg,kv,atlas,json # 版本 version 0.1 # 依赖Kivy 必须写 requirements python3,kivy # 权限按需添加 android.permissions INTERNET,WRITE_EXTERNAL_STORAGE # API 级别 android.api 33 android.minapi 21 android.ndk 25b # 架构建议只打 arm64-v8a减小体积 android.archs arm64-v8a几个容易踩坑的点requirements里如果要加第三方库必须确认这个库有 Android 版本否则构建会失败。纯 Python 库一般没问题带 C 扩展的库就要小心了。android.archs如果写多个架构APK 体积会翻倍。现在主流手机都是 arm64-v8a只打这一个就够了。android.api和android.ndk的版本要匹配不匹配会报错。NDK 25b 配 API 33 是比较稳的组合。4.4 构建过程和常见报错配置好后运行buildozer -v android debug第一次构建会下载 Android SDK、NDK 等一大堆东西可能要几十分钟甚至更久取决于网络。构建过程中最常见的报错有几类第一类是网络问题。Buildozer 要从 Google 的服务器下载 SDK 和 NDK网络不稳定就会失败。解决办法是提前手动下载好放到对应的缓存目录里。第二类是依赖编译失败。某个第三方库在 Android 上编译不过通常是缺少 C 库或者编译参数不对。这时候要么找替代库要么自己写 recipe。第三类是内存不足。编译过程很吃内存如果机器内存小于 8G可能会被 OOM Killer 干掉。加个 swap 分区能缓解。构建成功后APK 会生成在bin/目录下。debug 版的 APK 可以直接安装测试但要发布到应用商店还需要签名。4.5 APK 签名发布前的最后一步debug 版 APK 用的是调试签名不能用于正式发布。正式发布需要用自己的密钥签名# 生成密钥库 keytool -genkey -v -keystore myapp.keystore -alias myapp -keyalg RSA \ -keysize 2048 -validity 10000 # 签名 jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 \ -keystore myapp.keystore bin/myapp-0.1-debug.apk myapp密钥库文件一定要保管好丢了就没法给同一个应用发布更新了。我建议把密钥库和密码存在安全的地方并且做好备份。5. 那些文档里不会写的坑前面讲的都是流程性的东西这一节专门讲我实际踩过的坑这些内容你在官方文档里基本找不到。5.1 杀毒软件误报最让人头疼的问题PyInstaller 打包出来的 EXE被 Windows Defender 或者某些杀毒软件误报是极其常见的情况。原因有几个打包后的 EXE 结构特殊、引导程序的行为模式类似某些恶意软件、UPX 压缩特征等。应对方法尽量不用 UPX给 EXE 加上数字签名需要购买代码签名证书向杀毒软件厂商提交误报申诉如果只是内部使用把程序目录加入白名单说实话这个问题没有完美的解决方案。如果是商业软件买代码签名证书是最靠谱的。如果是个人项目提前跟用户说明情况让他们加白名单。5.2 路径中的中文和空格PyInstaller 对路径中的中文和空格支持不好有时候会莫名其妙地失败。打包时的工作目录以及 Python 安装路径最好都不要有中文和空格。这个坑我踩过好几次报错信息还特别隐晦排查半天才发现是路径问题。5.3 动态导入的库需要手动处理有些库用__import__或者importlib动态导入模块PyInstaller 静态分析发现不了。典型的有某些 ORM 框架插件系统配置驱动的模块加载解决办法是用--hidden-import手动指定或者在 spec 文件的 hiddenimports 里加上。如果不知道缺了哪个模块可以看运行时的报错信息它会告诉你 ModuleNotFoundError: No module named xxx。5.4 多进程程序的特殊处理如果你的程序用了 multiprocessing打包后会出问题。因为 multiprocessing 在 Windows 上会重新启动一个 Python 进程而打包后的 EXE 没有 Python 解释器路径。解决办法是在程序入口加上import multiprocessing if __name__ __main__: multiprocessing.freeze_support() # 你的主程序逻辑这个freeze_support()是专门为打包场景设计的不加的话多进程程序打包后必然出问题。5.5 打包 PaddleOCR 这类重型库的注意事项PaddleOCR 是很多人会用到的东西但它的打包特别麻烦。它依赖 paddlepaddle而 paddlepaddle 又依赖一大堆 C 库和模型文件。打包时要注意模型文件必须用--add-data打包进去paddle 的某些动态库需要手动指定打包体积会非常大轻松超过 500 MB我的经验是如果非必要不要把 PaddleOCR 打包进 EXE。可以考虑把 OCR 功能做成服务端接口客户端只负责调用这样能大幅减小客户端体积。6. 从打包到交付一些实战建议打包只是交付的第一步真正把软件交到用户手里还有一些细节要注意。6.1 版本信息和文件属性打包出来的 EXE右键属性里可以看到版本信息。默认情况下这些是空的显得不专业。可以通过 spec 文件或者版本信息文件来填充# 在 spec 文件中 exe EXE( ..., versionversion_info.txt, )version_info.txt 是一个特定格式的文本文件包含公司名、产品名、版本号等信息。填好之后EXE 的属性面板就完整了。6.2 打包后的完整测试清单打包完成后不要只在自己电脑上测一下就算完。我建议至少做这些测试在一台没装 Python 的干净电脑上运行测试所有功能尤其是涉及文件读写、网络请求的部分测试中文路径和带空格的路径测试杀毒软件是否误报测试不同 Windows 版本Win10、Win11有条件的话用虚拟机做干净环境测试是最靠谱的。6.3 自动化打包脚本如果项目需要频繁打包手动敲命令太累。写个批处理或者 shell 脚本把打包命令固化下来#!/bin/bash # build.sh source venv/bin/activate rm -rf build dist pyinstaller main.spec echo 打包完成输出在 dist/ 目录配合 CI 工具还能实现提交代码自动打包省时省力。6.4 关于代码保护的一点实话很多人打包是为了保护源码但说实话PyInstaller 打包的 EXE 是可以被反编译的。有专门的工具能把打包后的 pyc 文件还原成接近原始的 Python 代码。如果你的代码有商业价值光靠 PyInstaller 是不够的。真正有效的代码保护方案包括用 Nuitka 编译成机器码、核心逻辑放到服务端、使用商业加密工具。但这些都有各自的成本和限制。我的建议是普通工具类软件PyInstaller 足够了核心算法有商业价值的认真考虑服务端方案。打包这件事说难不难说简单也不简单。工具就那几个命令也就那些但真正决定成败的是对细节的把控——虚拟环境干不干净、资源路径处理对不对、依赖有没有遗漏、目标平台的环境差异考虑没考虑到。我这些年打包过的东西从几十行的小脚本到上万行的桌面应用都有最大的体会就是打包不是最后一步而是从项目一开始就要考虑的事。你在写代码的时候就要想着资源怎么放、依赖怎么管、路径怎么写等到要打包了才临时抱佛脚坑会多得多。另外别怕报错打包过程中的报错信息虽然有时候很晦涩但每解决一个你对 Python 运行机制的理解就深一层。
企业数字化 ERP 产品动态
相关推荐
agent-native实战:如何把系统改造成AI Agent的第一公民 去年年底,我和团队在做一个企业知识库的AI助手时遇到了一个非常典型的瓶颈:模型能力已经足够强,prompt也调到了一定水平,但系统就是“不好用”。问题出在哪儿?出在系统根本就不是为智能体设计的。我们的CRM、工单系统、… · 2026/9/26 13:15:52
对称加密与非对称加密怎么选?国密SM1/SM2/SM3工程配合全解析 上周评审一个加密传输方案,负责接口对接的同学当场问我:老哥,咱们到底用对称加密还是非对称加密?你先给我个准话。这个问题几乎每次做安全设计都会被问一次,但说实话,它本身没有标准答案——因为任何一套正… · 2026/9/26 13:15:52
短视频无水印素材下载与整理:三步实操指南 短视频素材的收集整理,是很多做内容的朋友绕不开的一道坎。你刷到一个特别适合做混剪的片段,或者看到一个值得收藏的干货讲解,想把它存下来做二次创作,结果下载下来一看,画面角落稳稳地压着一个平台水印,位… · 2026/9/26 13:52:49
云服务器部署实战:从Docker到AI模型的完整学习路径与避坑指南 1. 折腾部署的时候,我先被本机环境磨掉了耐心今年上半年,我的主要学习内容就是"部署系统"。从最简单的docker run hello-world,到 GitLab 社区版、Zabbix 监控平台、再到 AI 模型的本地部署实验,一路走下来最大的感受不… · 2026/9/26 13:52:43
端到端心电事件识别实战包:QRS检测+多类分类+临床部署 简介:本资源是山东第三届数据应用创新创业大赛‘心电图智能事件识别’赛道的亚军技术方案,面向医学AI、生物信号处理及机器学习方向的开发者与高校学生,聚焦ECG时序信号中异常事件(如心律失常)的自动识别任务。压缩包共… · 2026/9/26 13:52:37
基于SpringBoot+Vue的数字化农家乐管理平台实战:从架构设计到避坑指南 简介:这是一套面向高校计算机专业学生与Java全栈开发者的数字化农家乐管理平台毕业设计资源包,基于Java、SpringBoot、Vue与MySQL技术栈构建,可直接用于毕设、课程设计或期末大作业,下载即用无需修改。压缩包共858个文件ÿ… · 2026/9/26 13:52:37
CATIA参数化建模中参数不显示在结构树的解决方法 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 13:52:30
Python requests库办公自动化实战:批量查询、数据抓取与报表下载 你们有没有遇到过这种情况:领导甩过来一张Excel表格,里面躺着几十个订单号,让你挨个去快递官网查物流状态,查完再把结果填回去。手动打开网页、复制单号、点查询、复制结果、粘贴到表格,一个单号折腾两三分钟ÿ… · 2026/9/26 13:52:24
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍 简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践 一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46