Python 打包概述#

作为一门通用编程语言,Python 被设计为可以以多种方式使用。你可以构建网站、工业机器人或供朋友玩的游戏,以及更多内容,所有这些都使用相同的核心技术。

Python 的灵活性是每个 Python 项目的第一步必须考虑项目受众和项目将运行的相应环境的原因。在编写代码之前考虑打包似乎很奇怪,但此过程可以很好地避免未来的麻烦。

本概述提供了一个通用决策树,用于推理 Python 的大量打包选项。继续阅读,为你的下一个项目选择最佳技术。

考虑部署#

包的存在是为了安装(或部署),因此在你打包任何内容之前,你需要对以下部署问题有一些答案

  • 你的软件用户是谁?你的软件将由从事软件开发的其他开发人员、数据中心的运营人员还是不太精通软件的群体安装?

  • 你的软件打算在服务器、台式机、移动客户端(手机、平板电脑等)上运行,还是嵌入在专用设备中?

  • 你的软件是单独安装还是大批量部署?

打包完全取决于目标环境和部署体验。对于以上问题有很多答案,每种情况组合都有自己的解决方案。有了这些信息,以下概述将指导你选择最适合你的项目的打包技术。

打包 Python 库和工具#

你可能听说过 PyPI、setup.pywheel 文件。这些只是 Python 生态系统为开发人员分发 Python 代码提供的众多工具中的一部分,你可以在 打包和分发项目 中阅读相关内容。

以下打包方法适用于技术受众在开发环境中使用的库和工具。如果你正在寻找为非技术受众和/或生产环境打包 Python 的方法,请跳至 打包 Python 应用程序

Python 模块#

只要 Python 文件仅依赖于标准库,就可以重新分发和重复使用。你还需要确保它为正确的 Python 版本编写,并且仅依赖于标准库。

这非常适合在 Python 版本兼容的人员之间共享简单的脚本和片段(例如通过电子邮件、StackOverflow 或 GitHub gist)。甚至有一些完整的 Python 库提供此选项,例如 bottle.pyboltons

但是,此模式不适用于由多个文件组成、需要其他库或需要特定 Python 版本的项目,因此有了以下选项。

Python 源分发#

如果您的代码包含多个 Python 文件,通常会将其组织到目录结构中。任何包含 Python 文件的目录都可以包含一个 导入包

由于包由多个文件组成,因此它们更难分发。大多数协议仅支持一次传输一个文件(您上次单击链接并下载多个文件是什么时候?)。更容易获得不完整的传输,并且更难保证目标代码的完整性。

只要您的代码不包含任何纯 Python 代码,并且您知道您的部署环境支持您的 Python 版本,那么您可以使用 Python 的原生打包工具创建一个 分发包,或简称sdist

Python 的sdist 是压缩存档(.tar.gz 文件),包含一个或多个包或模块。如果您的代码是纯 Python,并且您仅依赖于其他 Python 包,您可以转到 源分发格式 规范以了解更多信息。

如果您依赖于任何非 Python 代码或非 Python 包(例如 libxml2lxml 的情况下,或 BLAS 库在 numpy 的情况下),您将需要使用下一节中详述的格式,该格式对于纯 Python 库也具有许多优点。

注意

Python 和 PyPI 支持多个分发,提供同一包的不同实现。例如,无人维护但具有开创性的 PIL 分发 提供 PIL 包,Pillow 也提供 PIL 包,它是 PIL 的一个积极维护的分支!

此 Python 打包超级功能使 Pillow 能够通过仅更改项目的 install_requiresrequirements.txt 来替代 PIL。

Python 二进制分发#

Python 的实用功能如此强大,很大程度上是因为它能够与软件生态系统集成,特别是用 C、C++、Fortran、Rust 和其他语言编写的库。

并非所有开发人员都拥有构建用这些编译语言编写的这些组件的正确工具或经验,因此 Python 创建了 Wheel,这是一种旨在使用编译工件运送库的包格式。事实上,Python 的包安装程序 pip 始终更喜欢 wheels,因为安装总是更快,因此即使是纯 Python 包也能更好地与 wheels 配合使用。

二进制分发在与源分发匹配时效果最佳。即使您不会为每个操作系统上传代码的 wheels,但通过上传 sdist,您仍能使用其他平台的用户为自己构建它。默认情况下同时发布 sdist 和 wheel 存档,除非 您正在为非常特定的用例创建工件,并且您知道接收方只需要其中一个。

Python 和 PyPI 使得同时上传 wheels 和 sdist 变得容易。只需按照 打包 Python 项目 教程进行操作。

A summary of Python's packaging capabilities for tools and libraries.

Python 推荐的内置库和工具打包技术。摘自 打包梯度 (2017)#

打包 Python 应用程序#

到目前为止,我们只讨论了 Python 的原生分发工具。根据我们的介绍,您可以正确地推断出这些内置方法仅针对具有 Python 的环境,以及知道如何安装 Python 包的受众。

对于各种操作系统、配置和人员,只有在针对开发人员受众时,此假设才安全。

Python 的原生打包主要用于在开发人员之间分发可重用代码(称为库)。您可以使用 setuptools entry_points 等技术,在 Python 的库打包之上搭载工具或面向开发人员的基本应用程序。

库是构建块,而不是完整的应用程序。对于分发应用程序,那里有一个全新的技术世界。

接下来的几节根据应用程序打包选项对目标环境的依赖性对这些选项进行组织,以便您可以为您的项目选择合适的选项。

依赖于框架#

某些类型的 Python 应用程序,例如网站后端和其他网络服务,非常常见,以至于它们有框架来启用其开发和打包。其他类型的应用程序,例如动态 Web 前端和移动客户端,其目标足够复杂,以至于框架变得不仅仅是一种便利。

在所有这些情况下,从框架的打包和部署故事中倒推是有意义的。一些框架包括一个部署系统,该系统封装了本指南其余部分中概述的技术。在这些情况下,您需要参考框架的打包指南,以获得最简单、最可靠的生产体验。

如果您想知道这些平台和框架在底层如何工作,您始终可以阅读后面的部分。

服务平台#

如果您正在为“平台即服务”或“PaaS”开发,您将需要遵循它们各自的打包指南。只要您遵循它们的模式,这些类型的平台就会负责打包和部署。大多数软件不适合其中一个模板,因此存在以下所有其他选项。

如果您正在开发将部署到您自己、用户个人电脑或任何其他安排的计算机上的软件,请继续阅读。

Web 浏览器和移动应用程序#

Python 的稳步发展正在将其带入新的领域。如今,您可以在 Python 中编写移动应用程序或 Web 应用程序前端。虽然语言可能是熟悉的,但打包和部署实践是全新的。

如果您计划发布到这些新领域,您需要查看以下框架,并参考它们的打包指南

如果您有兴趣使用框架或平台,或者只是想知道上述框架使用的一些技术和方法,请继续阅读。

依赖于预安装的 Python#

选择一台任意计算机,根据上下文,Python 很可能已经安装。多年来,大多数 Linux 和 Mac 操作系统都默认包含 Python,您有理由依赖于数据中心或开发人员和数据科学家的个人计算机中预先存在的 Python。

支持此模型的技术

  • PEX(Python 可执行文件)

  • zipapp(不帮助管理依赖项,需要 Python 3.5+)

  • shiv(需要 Python 3)

注意

在所有这些方法中,依赖于预安装的 Python 最依赖于目标环境。当然,这也使得包最小,小到个位数兆字节,甚至千字节。

一般来说,减少对目标系统的依赖会增加我们包的大小,因此这里的解决方案大致按输出大小增加的顺序排列。

依赖于单独的软件分发生态系统#

很长一段时间,包括 Mac 和 Windows 在内的许多操作系统都缺乏内置的包管理。直到最近,这些操作系统才获得了所谓的“应用商店”,但即使这些商店也专注于消费者应用程序,并且为开发人员提供的支持很少。

开发人员长期寻求补救措施,在这场斗争中,他们提出了自己的包管理解决方案,例如 Homebrew。对于 Python 开发人员来说,最相关的替代方案是一个名为 Anaconda 的包生态系统。Anaconda 是围绕 Python 构建的,在学术、分析和其他面向数据的环境中越来越普遍,甚至进入了 面向服务器的环境

针对 Anaconda 生态系统构建和发布的说明

类似的模型涉及安装替代的 Python 发行版,但不支持任意操作系统级别的包

自带 Python 可执行文件#

正如我们所知,计算由执行程序的能力定义。每个操作系统本机支持一种或多种可以本机执行的程序格式。

有许多技术和工具可以将 Python 程序转换为其中一种格式,其中大多数涉及将 Python 解释器和任何其他依赖项嵌入到单个可执行文件中。

这种称为“冻结”的方法提供了广泛的兼容性和无缝的用户体验,尽管通常需要多种技术和大量的精力。

Python 冻结程序选择

上述大多数都意味着单用户部署。对于多组件服务器应用程序,请参阅 Chef Omnibus

自带用户空间#

越来越多的操作系统(包括 Linux、Mac OS 和 Windows)可以设置为运行打包为轻量级映像的应用程序,使用一种相对现代的机制,通常称为 操作系统级虚拟化 或“容器化”。

这些技术大多与 Python 无关,因为它们打包整个操作系统文件系统,而不仅仅是 Python 或 Python 包。

在 Linux 服务器中采用最为广泛,该技术起源于此,并且以下技术在此处效果最佳

自带内核#

大多数操作系统都支持某种形式的经典虚拟化,运行打包为包含自己完整操作系统的映像的应用程序。运行这些虚拟机或 VM 是一种成熟的方法,在数据中心环境中广泛使用。

这些技术主要保留用于数据中心中的更大规模部署,尽管某些复杂应用程序可以受益于此打包。这些技术与 Python 无关,包括

自带硬件#

交付软件最全面的一种方式是将其预装在某些硬件上交付。这样,软件用户只需要电即可。

虽然上面描述的虚拟机主要为技术人员保留,但你可以发现从最先进的数据中心到最年幼的儿童都在使用硬件设备。

将你的代码嵌入 AdafruitMicroPython 或运行 Python 的更强大的硬件中,然后将其运送到数据中心或用户家中。他们即插即用,你可以结束这一天了。

A summary of technologies used to package Python applications.

用于打包 Python 应用程序的技术的简化范围。#

关于…#

以上各节只能总结这么多,你可能想知道一些更明显的差距。

操作系统包#

如上文 依赖于单独的软件分发生态系统 中所述,一些操作系统有自己的包管理器。如果你非常确定你所针对的操作系统,你可以直接依赖于 deb(适用于 Debian、Ubuntu 等)或 RPM(适用于 Red Hat、Fedora 等)之类的格式,并使用该内置包管理器来处理安装,甚至部署。你甚至可以使用 FPM 从同一来源生成 deb 和 RPM。

在大多数部署管道中,操作系统包管理器只是其中的一部分。

virtualenv#

Virtualenvs 一直是多代 Python 开发者的必备工具,但随着它们被更高级别的工具包装起来,它们逐渐淡出视野。特别是对于打包,virtualenvs 被用作 dh-virtualenv 工具osnap 中的原语,这两个工具都以自包含的方式包装 virtualenvs。

对于生产部署,不要依赖于从互联网运行 python -m pip install 到 virtualenv 中,就像在开发环境中所做的那样。上面的概述充满了更好的解决方案。

安全性#

您在梯度上走得越远,更新软件包组件就变得越困难。所有内容都更紧密地结合在一起。

例如,如果出现内核安全问题,并且您正在部署容器,则可以更新主机系统的内核,而无需代表应用程序进行新的构建。如果您部署 VM 映像,则需要进行新的构建。这种动态是否使一种选择更安全仍然是一个有点老生常谈的问题,可以追溯到 静态与动态链接 的问题,该问题仍未解决。

总结#

Python 中的打包有点声名狼藉,因为它是一段坎坷的旅程。这种印象主要是 Python 多功能性的副产品。一旦您了解了每个打包解决方案之间的自然界限,您就会开始意识到,对于 Python 程序员来说,使用最平衡、最灵活的语言之一所付出的代价就是这种多变的格局。