打包的好处:不需要安装python即可直接运行,将py程序和依赖包打到一个二进制文件里,不需要安装任何依赖,支持多平台,适合分发;
常用的打包工具:
pyinstaller,cx_Freeze(支持多平台)
py2exe,pynsist(只支持win平台)
py2app(支持macos平台)
bbFreeze(只支持win,linux平台上的py2版本)
osnap(只支持win和mac平台)
打包工具官方网站及状态
http://www.pyinstaller.org/ # 持续更新中
https://cx-freeze.readthedocs.io/en/latest/ # 持续更新中
https://github.com/py2exe/py2exe # 近3个月未更新
https://pynsist.readthedocs.io/en/latest/ # 近2个月未更新
https://github.com/ronaldoussoren/py2pp # 持续更新中
https://github.com/schmir/bbfreeze # 废弃
https://github.com/jamesabel/osnap # 废弃
pyinstaller介绍
是一个跨平台的打包程序,目前已支持win|linux|macos|freebsd|solaris|aix等多个平台的可执行程序打包,配置简单;
pip install pyinstaller # python3.6+
pyinstaller --version # 4.5.1
vim pyinstallerDemo.py
pyinstaller ./pyinstallerDemo.py # dist/pyinstallDemo/pyinstallerDemo.exe,单目录格式
或
pyinstaller -F ./pyinstallerDemo.py # 单文件格式
yum -y install python38-devel # linux平台
注,目标目录下文件名和目录名不能相同,否则会报错
pyinstaller原理:
查找文件-查找模块,在脚本里找所有import语句,然后根据import语句后面的模块名找到对应的模块位置,再迭代查找,直到找到所有需要的模块;
查找文件-查找py解释器,找到你的开发环境设置的py解释器路径,然后将py解释器文件及所需的依赖文件都复制过来;
查找文件-查找时需要注意的问题,pyinstaller是根据import语句来查找模块,而在py中除了import语句以外,还有另外的方式可导入模块:__import__()函数、importlib.import_module(),如果遇到这种情况会看到报错No module found,解决办法:改为import语句导入模块|在打包时通过命令行选项指定模块路径|编辑spec文件告诉pyinstaller需要额外导入哪些模块;
两种格式:
单目录格式,默认,适合大型项目,即把所有的文件和依赖打包到一个目录里去,然后在目录下可执行打包好的exe文件,这种模式有2个优点(非常适合排错,因为在目录里你可以清楚的看到包含了哪些模块;在更新代码时,只需要更新重新打包后的exe文件,而单目录格式下这个文件非常小,因此在大规模分发时非常有优势);劣势(在大量依赖中找到需要的那个可执行文件比较麻烦;不小心会删掉目录中的任意文件会导致无法运行);
单文件格式,适合脚本或小项目,是把所有依赖和py脚本打包到一个exe文件里,实际使用时直接双击这个exe文件或/path/xxx.exe调用,单文件模式执行时,它首先会在机器上创建一个临时目录,然后使用内部打包的py解释器在这个临时目录下启动一个临时py环境,在这个环境里执行py脚本,同时在打包的文件内部查找打包进来的模块,还会查找需要的文件,有时还需要解压打包的文件,因此单个文件模式可能会比单目录格式要慢些;优势(只有单个可执行文件、使用简单方便;不担心删除单个文件导致无法执行的情况);劣势(单个打包文件在依赖多时,会非常大,需要使用压缩措施;创建的临时目录在程序被杀死时不会自动删除,因此在应用频繁崩溃时,会大量消耗硬盘空间,需要手动清理);
使用spec文件:
在单目录格式或单文件格式,都会生成一个.spec文件,这个文件是用来告诉pyinstaller怎么处理你的脚本,pyinstaller通过执行这个spec文件里的内容来构建app,可理解为linux里打rpm包时使用的spec文件;
在大部分场景下,基本上都不需要主动编写或修改这个文件,实际应用场景有:需要在app里打包数据文件(如包含sqlite数据库文件);需要从其他位置包含.dll或.so文件;需要添加py运行时参数到可执行文件里;