Python 打包概述¶
作为一种通用编程语言,Python 被设计为以多种方式使用。你可以构建网站、工业机器人或为你朋友玩的游戏,以及更多,所有这些都使用相同的核心技术。
Python 的灵活性是每个 Python 项目的第一步必须考虑项目受众和项目将运行的相应环境的原因。在编写代码之前考虑打包可能看起来很奇怪,但这对于避免未来的麻烦大有裨益。
本概述提供了一个通用决策树,用于思考 Python 众多的打包选项。继续阅读以选择最适合你的下一个项目的技术。
考虑部署¶
包是为了安装(或部署)而存在的,因此在你打包任何东西之前,你需要对下面的部署问题有一些答案。
你的软件的用户是谁?你的软件将由进行软件开发的开发人员、数据中心的运维人员还是对软件不太了解的群体安装?
你的软件是打算在服务器、桌面、移动客户端(手机、平板电脑等)上运行,还是嵌入在专用设备中?
你的软件是单独安装,还是批量部署?
打包一切都关乎目标环境和部署体验。上述问题有很多答案,每种情况组合都有其自身的解决方案。有了这些信息,下面的概述将引导你选择最适合你项目的打包技术。
打包 Python 库和工具¶
你可能听说过 PyPI、setup.py 和 wheel 文件。这些只是 Python 生态系统为向开发人员分发 Python 代码而提供的工具中的一部分,你可以在打包和分发项目中阅读更多内容。
以下打包方法适用于开发环境中技术受众使用的库和工具。如果你正在寻找为非技术受众和/或生产环境打包 Python 的方法,请跳到打包 Python 应用程序。
Python 模块¶
一个 Python 文件,只要它只依赖标准库,就可以重新分发和重用。你还需要确保它是为正确版本的 Python 编写的,并且只依赖于标准库。
这非常适合在拥有兼容 Python 版本的人之间共享简单的脚本和片段(例如通过电子邮件、StackOverflow 或 GitHub gists)。甚至有一些完整的 Python 库提供此选项,例如bottle.py和boltons。
然而,这种模式不适用于由多个文件组成、需要额外库或需要特定 Python 版本的项目,因此有了下面的选项。
Python 源码分发¶
如果你的代码由多个 Python 文件组成,它通常会组织成一个目录结构。任何包含 Python 文件的目录都可以构成一个导入包。
由于包由多个文件组成,因此它们更难分发。大多数协议只支持一次传输一个文件(你上次点击链接并下载多个文件是什么时候?)。这更容易导致不完整的传输,并且更难保证目标处的代码完整性。
只要你的代码只包含纯 Python 代码,并且你知道你的部署环境支持你的 Python 版本,那么你就可以使用 Python 的原生打包工具来创建一个源码分发包,简称sdist。
Python 的 sdists 是压缩档案(.tar.gz 文件),包含一个或多个包或模块。如果你的代码是纯 Python 的,并且你只依赖于其他 Python 包,你可以前往源码分发格式规范了解更多。
如果你依赖任何非 Python 代码或非 Python 包(例如libxml2(在lxml的情况下)或 BLAS 库(在numpy的情况下)),你将需要使用下一节中详细介绍的格式,这种格式对于纯 Python 库也具有许多优点。
Python 二进制分发¶
Python 实际力量的很大一部分来自于它与软件生态系统集成的能力,尤其是用 C、C++、Fortran、Rust 和其他语言编写的库。
并非所有开发人员都拥有构建这些用编译语言编写的组件的正确工具或经验,因此 Python 创建了Wheel,这是一种旨在随编译工件一起发布库的包格式。事实上,Python 的包安装程序 pip 总是更喜欢 wheel,因为安装总是更快,所以即使是纯 Python 包使用 wheel 也效果更好。
二进制分发最好与源码分发配套提供。即使你不为每个操作系统上传你代码的 wheel,通过上传 sdist,你仍然能让其他平台的用户自行构建它。默认情况下,一起发布 sdist 和 wheel 档案,除非你正在为非常具体的用例创建工件,并且你知道接收者只需要其中之一。
Python 和 PyPI 使同时上传 wheel 和 sdist 变得容易。只需遵循打包 Python 项目教程即可。
Python 推荐的内置库和工具打包技术。摘自打包梯度 (2017)。¶
打包 Python 应用程序¶
到目前为止,我们只讨论了 Python 的原生分发工具。根据我们的介绍,你正确地推断出这些内置方法只针对拥有 Python 的环境,以及知道如何安装 Python 包的受众。
考虑到各种操作系统、配置和人员,只有针对开发人员受众时,这种假设才是安全的。
Python 的原生打包主要用于在开发人员之间分发可重用代码,称为库。你可以利用setuptools 入口点等技术,将工具或开发人员的基本应用程序附加到 Python 的库打包之上。
库是构建块,而不是完整的应用程序。对于分发应用程序,存在一个全新的技术世界。
接下来的几节将根据这些应用程序打包选项对目标环境的依赖关系进行组织,以便你可以为你的项目选择正确的选项。
依赖框架¶
某些类型的 Python 应用程序,如网站后端和其他网络服务,由于足够常见,所以有框架来支持它们的开发和打包。其他类型的应用程序,如动态网络前端和移动客户端,由于目标复杂,框架变得不仅仅是一种便利。
在所有这些情况下,从框架的打包和部署方面逆向工作是有意义的。一些框架包含一个部署系统,它封装了指南其余部分概述的技术。在这些情况下,你会希望参考你的框架的打包指南,以获得最简单、最可靠的生产体验。
如果你想知道这些平台和框架在底层是如何工作的,你随时可以阅读后面的章节。
服务平台¶
如果你正在为“平台即服务”或“PaaS”开发,你会希望遵循它们各自的打包指南。只要你遵循它们的模式,这些类型的平台就会负责打包和部署。大多数软件不符合这些模板之一,因此存在下面所有其他选项。
如果你正在开发的软件将部署到你拥有的机器、用户的个人计算机或任何其他安排,请继续阅读。
Web 浏览器和移动应用程序¶
Python 的稳步发展正将其带入新的领域。如今,你可以用 Python 编写移动应用程序或 Web 应用程序前端。虽然语言可能很熟悉,但打包和部署实践是全新的。
如果你打算发布到这些新领域,你会希望查看以下框架,并参考它们的打包指南
如果你不打算使用框架或平台,或者只是想了解上述框架使用的一些技术和技巧,请继续阅读以下内容。
依赖于预安装的 Python¶
随意选择一台计算机,根据上下文,很有可能已经安装了 Python。作为多年来大多数 Linux 和 Mac 操作系统中的默认组件,你可以合理地依赖你的数据中心或开发人员和数据科学家的个人机器上预先存在的 Python。
支持此模型的技术
注意
在所有这些方法中,依赖预安装的 Python 对目标环境的依赖性最大。当然,这也使得包最小,小到个位数兆字节,甚至千字节。
一般来说,减少对目标系统的依赖会增加包的大小,因此这里的解决方案大致按输出大小递增排列。
依赖于单独的软件分发生态系统¶
长期以来,包括 Mac 和 Windows 在内的许多操作系统都缺乏内置的包管理。直到最近,这些操作系统才获得了所谓的“应用商店”,但即使这些应用商店也主要关注消费者应用程序,对开发人员提供的功能很少。
开发者们长期寻求补救措施,在这种斗争中,他们发展出了自己的包管理解决方案,例如 Homebrew。对 Python 开发者来说最相关的替代方案是一个名为 Anaconda 的包生态系统。Anaconda 围绕 Python 构建,在学术、分析和其他数据导向的环境中越来越常见,甚至也进入了面向服务器的环境。
在 Anaconda 生态系统中构建和发布说明
类似的模型涉及安装替代的 Python 分发,但不支持任意操作系统级别的包
自带 Python 可执行文件¶
我们所知的计算是由执行程序的能力来定义的。每个操作系统都原生支持一种或多种可以原生执行的程序格式。
有许多技术和方法可以将你的 Python 程序转换为这些格式之一,其中大多数涉及将 Python 解释器和任何其他依赖项嵌入到单个可执行文件中。
这种方法被称为冻结,它提供了广泛的兼容性和无缝的用户体验,尽管通常需要多种技术和大量的努力。
精选 Python 打包工具
pyInstaller - 跨平台
cx_Freeze - 跨平台
constructor - 用于命令行安装程序
py2exe - 仅限 Windows
py2app - 仅限 Mac
osnap - Windows 和 Mac
pynsist - 仅限 Windows
上述大多数都意味着单用户部署。对于多组件服务器应用程序,请参见 Chef Omnibus。
自带用户空间¶
越来越多的操作系统——包括 Linux、Mac OS 和 Windows——可以设置为运行打包成轻量级镜像的应用程序,使用一种相对现代的安排,通常被称为操作系统级虚拟化,或容器化。
这些技术大多与 Python 无关,因为它们打包的是整个操作系统文件系统,而不仅仅是 Python 或 Python 包。
在 Linux 服务器上应用最为广泛,因为该技术起源于此,并且以下技术在此处效果最佳
自带内核¶
大多数桌面操作系统支持某种形式的经典虚拟化,运行打包为包含完整操作系统的映像的应用程序。运行这些虚拟机(VM)是一种成熟的方法,在数据中心环境中广泛使用。
这些技术主要用于数据中心的大规模部署,尽管某些复杂的应用程序可以从这种打包中受益。这些技术与 Python 无关,包括
自带硬件¶
最全面地发布你的软件的方式是将其预安装在硬件上。这样,你的软件用户只需电源。
虽然上面描述的虚拟机主要保留给技术精通的人使用,但硬件设备被从最先进的数据中心到最年幼的孩子的所有人使用。
将你的代码嵌入到 Adafruit、MicroPython 或运行 Python 的更强大硬件上,然后将其交付到数据中心或你用户的家中。他们即插即用,你就可以收工了。
Python 应用程序打包技术的简化全貌。¶
那……呢?¶
上面的部分只能总结这么多,你可能会对一些更明显的空白感到好奇。
操作系统包¶
正如上面依赖于单独的软件分发生态系统中提到的,一些操作系统有自己的包管理器。如果你非常确定你目标操作系统的类型,你可以直接依赖诸如 deb(用于 Debian、Ubuntu 等)或 RPM(用于 Red Hat、Fedora 等)之类的格式,并使用该内置包管理器来处理安装甚至部署。你甚至可以使用 FPM 从相同来源生成 deb 和 RPM 包。
在大多数部署管道中,操作系统包管理器只是其中的一部分。
virtualenv¶
虚拟环境对于多代 Python 开发者来说一直是不可或缺的工具,但它们正在慢慢淡出视野,因为它们被更高级的工具封装起来。特别是在打包方面,虚拟环境被用作 dh-virtualenv 工具和 osnap 中的基本元素,这两个工具都以自包含的方式封装了虚拟环境。
对于生产部署,请不要像在开发环境中那样,通过互联网运行 python -m pip install 到虚拟环境。上面的概述提供了许多更好的解决方案。
安全¶
你越往下深入这个梯度,更新包组件就越困难。所有东西都结合得更紧密。
例如,如果出现内核安全问题,并且你正在部署容器,则可以更新主机系统的内核,而无需为应用程序重新构建。如果你部署 VM 映像,则需要重新构建。这种动态是否使某个选项更安全仍然是一个古老的争论,可以追溯到尚未解决的静态与动态链接问题。
总结¶
Python 中的打包有些名声不佳,被认为是一段颠簸的旅程。这种印象主要是 Python 多功能性的副产品。一旦你理解了每种打包解决方案之间自然的界限,你就会意识到,多样的环境是 Python 程序员为使用最平衡、最灵活的语言之一所付出的很小的代价。