12. 打包和发布

打包发布 Python 程序通常使用的工具有 zipappPyInstaller ,其中, zipapp 模块可用于生成可执行的 Python 包,这个包会包含目录下所有的 Python 程序,也可以将 Python 程序所依赖的模块下载到目标目录下,再生成可独立运行的 Python 程序,但是这样依旧依赖于 Python 的运行环境。 PyInstaller 工具则是将 Python 程序直接编译成不同平台上的可执行程序,这样就可以脱离 Python 的运行环境直接执行。

12.1. 使用 zipapp 模块

zipapp 是 Python 内置的一个模块,通过该模块可以将一个 Python 模块打包成一个 Python 应用。

12.1.1. 生成可执行的 Python 包

zipapp 模块的命令行语法如下:

python -m zipapp source [options]

其中, source 代表要打包的 Python 源程序或目录,既可以是单个的 Python 文件,也可以是文件夹。如果 source 参数是文件夹,那么 zipapp 模块会打包该文件夹中的所有 Python 文件。 options 支持如下选项:

  • -o <output> :指定输出的文件名。如果不指定该选项,所生成的文件名默认为 source 的值,并加上 .pyz 后缀
  • -p <interpreter> :指定 Python 解释器
  • -m <mainfunc> :指定 Python 程序的入口函数
  • -c :用于指定是否对档案包进行压缩来减小文件的大小,默认不压缩
  • --info :用于在诊断时显示包中的解释器
  • -h :用于显示 zipapp 模块的帮助信息

在此我们以一个简易的计算器程序为例,介绍 zipapp 模块的使用方法,在文件夹 chapter12 下创建源代码,简易计算器完成 +-*/% 的功能,新建 calculate.py 文件,具体代码如下:

def cal(a, b, ops):
	if ops == "+":
		return a + b
	elif ops == "-":
		return a - b
	elif ops == "*":
		return a * b
	elif ops == "/":
		return a / b
	elif ops == "%":
		return a % b
	else:
		# 抛出异常
		raise

同时,在该目录下新建一个 app.py 的程序,具体代码如下:

from calculate import *

if __name__ == "__main__":
	while True:
		try:
			a = float(input("请输入:a = "))
			b = float(input("请输入:b = "))
			ops = input("请输入运算符:")

			ret = cal(a, b, ops)
			print(str(a) + " " + ops + " " + str(b) + " = " + str(ret))
			break
		except Exception as e:
			print("输入错误,请重新输入。。。")
			continue

在命令行工具中进入 chapter12 目录下,并执行如下命令:

python3 -m zipapp app.py -o app.pyz

此时,在 chapter12 目录下生成了一个 app.pyz 文件,通过如下命令执行该程序:

python3 app.pyz

12.1.2. 生成独立应用

上述的打包方式只能打包当前的 Python 文件,对于其中依赖的文件则不能一起打包出来,如上述的 app.py 程序中依赖 calculate.py 的程序,将打包好的 app.pyz 移动到其他任意的文件夹下执行,就会报如下的错误:

Traceback (most recent call last):
  File "app.pyz", line 1, in <module>
    from calculate import *
ModuleNotFoundError: No module named 'calculate'

这种情况下,我们就需要对所有依赖的程序打包,需要如下的两个步骤:

  • 将应用依赖的模块和包下载到同一个目录中
  • 使用 zipapp 将应用和依赖模块一起打包

首先需要将所有依赖下载到同一个目录下,对于依赖的第三方库,需要使用到 pip install 语句安装到指定目录下,为做测试,我们在 chapter12 目录下新建一个 app 的文件夹,在 chapter12 新建一个 requirements.txt 的文件,用于填写需要下载的第三方库,此处我们不依赖其他的第三方库,在 chapter12 目录下执行以下命令:

python3 -m pip install -r requirements.txt --target app

此时还需要将所有的文件复制到 app 的文件夹下,此处需要的文件包括 calculate.pyapp.py ,同时将 app.py 改名为 __main__.py 用于标记程序的入口,在 chapter12 目录下再执行以下命令:

python -m zipapp app -o app.pyz

此时在 chapter12 目录下生成了 app.pyz 文件,将其移动到其他任意文件夹下都可以正常执行了。

12.2. 使用 PyInstaller 生成可执行程序

上述的打包方式依然依赖于 Python 的运行环境,还可以使用 PyInstaller 将 Python 程序生成可直接运行的程序。

12.2.1. PyInstaller 的安装

PyInstaller 模块并不是 Python 的内置模块,需要自行安装。使用 pip 的安装命令如下:

pip install pyinstaller

安装完成后,执行以下的命令以判断是否安装成功:

pyinstaller -V

如果安装成功,会出现如下的信息:

输入图片说明

12.2.2. 生成可执行程序

PyInstaller 工具的命令语法如下:

pyinstaller options source

不管这个 Python 应用是单文件的应用,还是多文件的应用,只要在使用 pyinstaller 命令时编译作为程序入口的 Python 程序即可。还是以上述简易计算器代码为例,将 calculate.py__main__.py 复制到文件夹 appinstaller 下,并执行以下的命令:

pyinstaller -D __main__.py

这里以 mac os 为例,等打包完成后就会在 appinstaller 文件夹下生成 __main__.spec 文件和 builddist 文件夹,如下图所示:

输入图片说明

最终生成的可执行文件在 dist 目录下生成名为 __main__ 可执行文件,双击该文件,就可以正常执行,运行结果如下图所示:

输入图片说明

至此已经完成了程序的打包,其中, options 参数除了 -D 以外,还有如下的一些可供选择:

输入图片说明

12.3. 本章小结

本章主要介绍了两种打包 Python 程序的工具: zipappPyInstaller ,其中, zipapp 主要用于将 Python 应用打包成一个可运行的 .pyz 文件,但是该文件依然需要 Python 环境来执行。而 PyInstaller 则直接将 Python 程序打包成可执行程序,而无需依赖 Python 环境。

本章需要掌握知识点:

  1. 掌握 zipapp 的两种打包方式
  2. 掌握 PyInstaller 打包方法