1. 简介
1.1. 打包原理
Python代码的基本运行过程:
- Python.exe调用XX.py(源码),解释并运行。
- Python.exe调用XX.pyc(字节码),解释并运行。
- Python.exe调用XX.pyd(机器码),调用运行。
- 如果有依赖的库,根据上面三种情况调用运行。
1.1.1. 方法一
分析脚本文件,递归找到所有依赖的模块。如果依赖模块有.pyd文件,即将其复制到disk目录。如果没有.pyd文件,则生成.pyc文件拷贝到disk目录,并压缩为.zip保存。制作一个exe,导入PythonXX.dll(解析器库),并添加exe运行需要的相关运行时库。这就构成了一个不用安装Python的运行包。
如果想生成一个exe,则利用PE结构,将整个包加载内存中,再来运行。
1.1.2. 方法二
.pyd是机器码文件。将.py转换成.pyd的机器码,有两种方式。一种是直接通过虚拟机生成机器码,这种方式,没有做代码优化,性能和虚拟机调用.pyc文件差不多。另一种方式,是将.py转换成而现C代码,C代码是最常见的可以直接编译生成机器码的语言。这样,可能利用C编译器本身的优化功能,可以大幅提高性能。
2. PyInstaller
PyInstaller就是基于方法一进行实现和优化的。
2.1. 安装
PyInstaller支持Win7及之后的操作系统。PyInstaller的运行需要PyWin32的Python扩展来支持。
pip install PyWin32
pip install PyInstaller
2.2. 用法
pyinstaller [options] script [script …] | specfile
-h | 显示帮助信息 |
-v | 显示进度信息 |
-y | 不显示确认信息 |
–clean | 清除缓存和临时文件 |
-D | 产生文件夹存放执行文件等,默认此设置 |
-F | 产生一个可执行文件 |
-n NMAE | NAME指定生成的exe名,默认第一个脚本名 |
-w | 不显示命令行窗口 |
–hidden-import MODULENAME | 有些库无法识别(如直接调用.py文件的方式),则需要指定,可以指定多次 |
–add-data SRC;DEST | 复制文本文件至指定目录 |
–add-binary SRC;DEST | 复制二进制文件到指定目录 |
–exclude-module EXCLUDES | 排除指定包或模块 |
–key KEY | 利用KEY作为密钥加密字节码 |
-i <file.ico or file.exe> | exe的图标 |
–version-file FILE | 版本信息 |
2.2.1. 优化代码
# run with basic optimizations
python -O -m PyInstaller myscript.py
# also discard docstrings
python -OO -m PyInstaller myscript.py
2.2.2. 示例
PyInstaller只需要指定最先执行的脚本文件即可。
- 打包成独立exe,使用当前目录下的test.ico,生成Spy.exe
PyInstaller -F -i test.ico -n Spy.exe test.py
- 打包到文件夹,不显式控制台窗口
PyInstaller -D -w test.py
- 打包到文件夹,添加配置文件flash.ini到当前目录,添加FTL.Dll到DLL目录。
PyInstaller -D --add-data flash.ini;. --add-binary FTL.DLL;DLL test.py
- 打包成独立exe,配置版本信息(版本信息参考pyi-grab_version)
PyInstaller -F --version-file ver.txt test.py
- 打包至文件夹,添加xlwt库,排除numpy库。
PyInstaller --hidden-import xlwt --exclude-module numpy test.py
- 优化打包,注意大小写
python -O -m PyInstaller test.py
3. Nuitka
Nuitka打包的原理基于方法二。Nuitka是一个Python编写的Python解释器,所以Nuitka可以作为Python的扩展库使用。Nuitka会将所有没有生成pyd的模块的.py代码转换成C代码,然后调用gcc/MSVC编译成.pyd(windows DLL)。Nuitka转换的C代码使用了C11,所以C编译器尽量用最新的。
3.1. 安装C编译器
下载MingW的64位和32位,解压到C盘,目录安装如下。
C:\MinGW64\mingw64\bin\gcc.exe
C:\MinGW64\mingw32\bin\gcc.exe
根据打包用的Python版本在环境变量中添加:
CC=C:\MinGW64\mingwXX\bin\gcc.exe
3.2. 安装Nuitka
pip install nuitka
3.3. 用法
–module | 将指定.py文件编译成.pyd文件 |
–standalone | 产生文件夹存放执行文件等 |
–windows-icon=ICON_PATH | 给exe添加图标 |
–windows-disable-console | 不显示控制台窗口 |
–recurse-all | 编译成独立exe |
3.4. 示例
- 默认只编译指定文件为exe
nuitka --mingw64 test.py
- 打包到文件夹
nuitka --mingw64 --standalone test.py
- 打包成一个exe
nuitka --mingw64 --recurse-all test.py
- 添加图标,并不显示控制台
nuitka --mingw64 --windows-icon=a.ico --windows-disable-console test.py
4. 减小打包大小
打包过大的原因是打包工具不能准确地识别所导入的库,导致收集了很多不需要的库。
4.1. 显式指明模块
imprt XX,隐式相对导入模块,打包工具会收集整个XX模块,导致打包可能收集到错误的模块。应该显式相对导入,即from YY import XX则只会收集YY库中XX模块。Python3.X中已经不推荐使用隐式相对导入。
4.2. 排除无关库
显式指明了导入的模块,打包工具还是有可能将扩展库中的其他没有用到的库也打包进来了。如果发现某些库是不需要的。PyInstaller可以通过–exclude-module EXCLUDES排除相关库。
4.3. 最少安装环境
有些无关库可以人工识别,但是有时复杂调用,人工识别较复杂。此时可以使用只安装使用的库,无关库不安装。这样打包工具就只会打包使用到的库。
4.3.1. 虚拟机环境
可以在VMWare等虚拟机上安装Python,然后安装打包需要的相关库,完成打包。
4.3.2. 虚拟环境
Python扩展库pipenv可以建立一个虚拟的Python环境,然后安装相关库,进行打包。
# 安装pipenv
pip install pipenv
# 建立虚拟环境
pipenv install
# 进入虚拟环境
pipenv shell
# 安装相关模块
pip install XXX
# 开始打包
pyinstaller ......
5. 注意
- 路径为英文,最好没有空格。
- .py文件使用utf-8编码。
- 使用显式导入,即from XX import YY,可以提高PyInstaller依赖库识别,降低打包大小。
- 有一些库有资源,PyInstaller不能使用的识别,需要手动将库拷贝到exe所在目录。如:pyecharts库,jieba库等,然后可以删除库中代码,只保留资源即可。
- Nuitka功能相比PyInstaller还有不足,也存在一些Bug。通常情况下使用PyInstaller打包,追求性能使用Nuitka打包。