Python打包工具大比拼:从入门到精通

Python打包工具大比拼:从入门到精通

首页休闲益智打包它们游戏更新时间:2024-05-09

—AI创造未来、科技改变生活(晴天AI,科技领域创作者)

作为一名Python开发者,当我第一次创建自己的包时,整个过程让我感到非常困惑。创建和管理包远比我想象的要复杂得多。更令人费解的是,市面上存在各种各样的工具,但我不知道该选择哪一个。我相信很多人一开始也有同样的感受。Python有无数的工具来管理虚拟环境和创建包,很难一下子搞清楚哪个最适合自己。虽然网上有一些相关的演讲和博客,但它们要么没有给出全景式的概览,要么缺乏系统性的评估。今天这篇文章,就是要带你全面了解Python包管理的方方面面。

工具分类

我将Python包相关的工具分为5大类:

1. Python版本管理

2. 包管理

3. 环境管理(主要是虚拟环境)

4. 包构建

5. 包发布

如下图所示,每一类都有不同的工具。有些是单兵作战,有些则是多面手:

接下来,让我们从开发者的视角逐一解析:

假设你同时负责公司项目和个人项目。公司的项目使用Python 3.7,而你希望个人项目用最新的Python 3.11。换句话说,你需要在不同Python版本间灵活切换。这就是Python版本管理工具的用武之地。

项目开发中,你会用到其他包,比如数据分析常用的pandas和sklearn。这些都是项目的依赖,你要负责安装和管理它们(比如在发布新版本时及时更新)。这就需要用到包管理工具了。

不同项目可能依赖同一个包的不同版本,为了避免冲突,你需要创建和管理虚拟环境。环境管理类工具就是干这个的。大部分工具使用虚拟环境,但也有些采用一种叫"本地包(Local Packages)"的概念,后面会讲到。

当你想跟别人分享代码时,首先得把它打包成一个包(包构建),然后才能发布到PyPI或其他索引平台供人下载安装(包发布)。

下面我们就深入每个类别,了解它们的定义、存在的意义以及可用的工具。单兵作战型和多面手型的工具,我会在最后分别详细介绍。让我们从Python版本管理开始。

Python版本管理

定义

Python版本管理工具让你轻松安装和切换不同的Python版本。

为什么要用到它

使用不同Python版本的原因有几个:

1. 你可能要维护多个项目,每个项目需要不同的Python版本。

2. 你的项目要兼容多个Python版本,为此你得在所有版本上测试。

3. 你想尝尝最新Python版本的新特性。

4. 你想测试一下Python预发布版本是否有bug。

可选工具

目前做Python版本管理的工具有pyenv、conda、rye和PyFlow。我们先看单兵作战的pyenv。

Python自带了一个专职做版本管理的工具:pyenv!它的用法非常简单,最常用的命令如下:

# 安装特定Python版本

pyenv install 3.10.4

# 为当前shell设置Python版本

pyenv shell <version>

# 为当前目录设置默认Python版本

pyenv local <version>

# 全局设置默认Python版本

pyenv global <version>

(虚拟)环境管理

定义

环境管理工具让你创建和管理(虚拟)环境。

为什么要用到它

我们为什么要用环境?前面提到,项目会依赖其他包。通常,不同项目会依赖同一个包的不同版本,这可能导致版本冲突。此外,直接用pip install安装包可能引发问题,因为这些包会安装到系统全局的Python环境里。其中部分问题可以通过--user选项解决,但这个用法并非人尽皆知,特别是新手。

可选工具

能创建和管理环境的工具有很多:venv、virtualenv、pipenv、conda、pdm、poetry、hatch、rye和PyFlow。其中venv和virtualenv是单兵作战型,我们先说这两个。

venv

venv(docs.python.org/3/library/v…)是Python内置的用于创建虚拟环境的包。最常用的命令如下:

# 创建新环境

python3 -m venv <env_name>

# 激活环境

. <env_name>/bin/activate

# 退出环境

deactivate

virtualenv

virtualenv(virtualenv.pypa.io/en/latest/)对venv做了改进,提供更多功能、更快的速度和更强的隔离性。最常用命令和venv类似:

# 创建新环境

virtualenv <env_name>

# 激活环境

. <env_name>/bin/activate

# 退出环境

deactivate

包管理

pyproject.toml

在讨论不同的打包工具前,我想确保你了解打包过程中最重要的文件:pyproject.toml。

Python打包领域近年来突飞猛进。在PEP 518之前,打包主要依靠setup.py文件,构建工具是setuptools。PEP 518引入了pyproject.toml文件的使用。现在创建包时,你必须提供一个pyproject.toml文件,用于定义项目的设置、元数据等内容。这里举个Pandas项目的pyproject.toml文件作为示例:

[build-system]

# Minimum requirements for the build system to execute.

# See https://github.com/scipy/scipy/pull/12940 for the AIX issue.

requires = [

"meson-python==0.13.1",

"meson==1.2.1",

"wheel",

"Cython==3.0.5", # Note: sync with setup.py, environment.yml and asv.conf.json

# Any NumPy version should be fine for compiling. Users are unlikely

# to get a NumPy<1.25 so the result will be compatible with all relevant

# NumPy versions (if not it is presumably compatible with their version).

# Pin <2.0 for releases until tested against an RC. But explicitly allow

# testing the `.dev0` nightlies (which require the extra index).

"numpy>1.22.4,<=2.0.0.dev0",

"versioneer[toml]"

]

build-backend = "mesonpy"

[project]

name = 'pandas'

dynamic = [

'version'

]

description = 'Powerful data structures for data analysis, time series, and statistics'

readme = 'README.md'

authors = [

{ name = 'The Pandas Development Team', email='pandas-dev@python.org' },

]

license = {file = 'LICENSE'}

requires-python = '>=3.9'

dependencies = [

"numpy>=1.22.4; python_version<'3.11'",

"numpy>=1.23.2; python_version=='3.11'",

"numpy>=1.26.0; python_version>='3.12'",

"python-dateutil>=2.8.2",

"pytz>=2020.1",

"tzdata>=2022.7"

]

classifiers = [

# 省略

锁定文件

除pyproject.toml,打包还有一个重要文件:锁定文件(xxx.lock)。

pyproject.toml里写的是抽象依赖,锁定文件里写的是具体依赖。它记录了为项目安装的所有依赖包的确切版本(如pandas==2.0.3)。这样能确保项目在不同机器上的一致性和可复现性。下面是poetry工具生成的一个poetry.lock文件片段示例:

# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.

[[package]]

name = "build"

version = "1.0.3"

description = "A simple, correct Python build frontend"

optional = false

python-versions = ">= 3.7"

files = [

{file = "build-1.0.3-py3-none-any.whl", hash = "sha256:589bf99a67df7c9cf07ec0ac0e5e2ea5d4b37ac63301c4986d1acb126aa83f8f"},

{file = "build-1.0.3.tar.gz", hash = "sha256:538aab1b64f9828977f84bc63ae570b060a8ed1be419e7870b8b4fc5e6ea553b"},

]

[package.dependencies]

colorama = {version = "*", markers = "os_name == \"nt\""}

importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""}

packaging = ">=19.0"

pyproject_hooks = "*"

tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}

[package.extras]

docs = ["furo (>=2023.08.17)", "sphinx (>=7.0,<8.0)", "sphinx-argparse-cli (>=1.5)", "sphinx-autodoc-typehints (>=1.10)", "sphinx-issues (>=3.0.0)"]

test = ["filelock (>=3)", "pytest (>=6.2.4)", "pytest-cov (>=2.12)", "pytest-mock (>=2)", "pytest-rerunfailures (>=9.1)", "pytest-xdist (>=1.34)", "setuptools (>=42.0.0)", "setuptools (>=56.0.0)", "setuptools (>=56.0.0)", "setuptools (>=67.8.0)", "wheel (>=0.36.0)"]

typing = ["importlib-metadata (>=5.1)", "mypy (>=1.5.0,<1.6.0)", "tomli", "typing-extensions (>=3.7.4.3)"]

virtualenv = ["virtualenv (>=20.0.35)"]

定义

包管理工具负责下载和安装库及其依赖。

为什么要用到它

我们为什么需要包?包允许我们以层级方式组织模块,使用"点号语法"方便地引用它们,比如

from package.module

import my_function

此外,它让我们能轻松地跟别人共享代码。每个包都有一个pyproject.toml文件定义了它的依赖,当别人安装这个包时,所需的依赖也会自动安装,他们不必再单独去安装每一个依赖包了。

可选工具

能做包管理的工具有不少:pip、pipx、pipenv、conda、pdm、poetry、rye和PyFlow。

其中,pip可能是最广为人知的,也是这里唯一的单兵作战选手。

Python标配的包管理器是pip(pip.pypa.io/en/stable/),它跟Python捆绑在一起,让你能从PyPI和其他索引平台安装包。最常用的命令(可能也是Python开发者学的第一个命令)是

pip install <package_name>

多面手工具

接下来我们进入复合型选手。

pipenv

顾名思义,pipenv把pip和virtualenv结合到一起,它能做虚拟环境管理和包管理这两件事。

pipenv引入了两个额外的文件:

- Pipfile:一个类似pyproject.toml的专门用于定义依赖的toml文件。

- Pipfile.lock:允许确定性构建。它消除了对requiremenets.txt文件的需求,自动进行锁定。

最常用的pipenv命令有:

# 安装包

pipenv install <package_name>

# 在虚拟环境中运行脚本

pipenv run <script_name.py>

# 激活虚拟环境

pipenv shell

conda

conda是一个通用的包管理系统。也就是说,它不局限于管理Python包。conda是一个功能强大的大*器,已经有无数的教程和博客介绍它的用法(如官方文档),这里就不赘述了。值得一提的是,尽管conda也能构建和发布包,但我没有在相应类别里列出它。这是因为conda的打包机制和常规的Python打包有所不同,它生成的是conda包。

特性评估

接下来我将从以下几个角度对比不同工具的特性:

- 该工具是否管理依赖?

- 是否解析和锁定依赖?

- 构建和发布流程是否清晰?

- 是否支持插件?

- 是否支持PEP 660(可编辑安装:peps.python.org/pep-0660/)?PEP 660是关于在pyproject.toml中基于构建的可编辑安装的提案。当你用pip install安装包时,可以加上-e选项,这样包就以可编辑模式安装。对开发者来说,这是个很重要的功能,你修改代码后,改动会立即反映到运行环境中,不必重新安装包。

- 是否支持PEP 621(项目元数据:peps.python.org/pep-0621/)?PEP 621规定了如何在pyproject.toml中编写项目的核心元数据。我之所以把它列为一个评判标准,是因为有个工具(剧透:是poetry)目前还不支持这个PEP,而是用自己的方式声明元数据。

flit

flit(flit.pypa.io/en/stable/)试图提供一种简洁的方式将Python包发布到PyPI。它有一个非常具体的使用场景:旨在为纯Python包(即没有构建步骤的包)服务。它不关心其他任何任务:

Python版本管理:❌

包管理:❌

环境管理:❌

构建包:✅

发布包:✅

该工具是否管理依赖?❌

是否解析和锁定依赖?❌

构建和发布流程是否清晰?✅

是否支持插件?❌

是否支持PEP 660?✅

是否支持PEP 621?✅

主要命令有:

# 创建新的pyproject.toml

flit init

# 构建并发布

flit publish

poetry

poetry可谓是大名鼎鼎。如维恩图所示,除了Python版本管理,其他事它都能干:

Python版本管理:❌

包管理:✅

环境管理:✅

构建包:✅

发布包:✅

poetry不支持PEP 621。GitHub上与此相关的issue已经开了大约1年半,但还没解决。

该工具是否管理依赖?✅

是否解析和锁定依赖?✅

构建和发布流程是否清晰?✅

是否支持插件?✅

是否支持PEP 660?✅

是否支持PEP 621?❌

主要命令:

# 创建目录结构和pyproject.toml

poetry new <project_name>

# 交互式创建pyproject.toml

poetry init

# 从pyproject.toml安装依赖

poetry install

# 依赖管理:

# 添加依赖

poetry add <package_name>

# 显示所有依赖

poetry show --tree

# 运行代码:

# 激活虚拟环境

poetry shell

# 在虚拟环境中运行脚本

poetry run python <script_name.py>

锁定文件:首次安装包时,poetry会解析pyproject.toml中列出的所有依赖并下载最新版本。安装完成后,poetry会将所有包及其下载的确切版本写入poetry.lock文件,将项目锁定到这些特定版本。建议将锁定文件提交到项目仓库,这样所有参与项目的人都使用相同版本的依赖。要将依赖更新到最新版本,请运行:

poetry update

构建/发布流程:

# 打包(创建.tar.gz和.whl)

poetry build

# 发布到PyPI

poetry publish

pdm

pdm是一个相对较新的包和依赖管理器(2019年首次发布),受poetry和PyFlow启发而生。你会注意到,本文没有讨论PyFlow。这是因为PyFlow已经停止开发,不再适应快速发展的打包工具领域。作为一个新兴工具,pdm要求Python 3.7 。pdm与其他工具的另一点不同是,它允许用户选择构建后端。

pdm是唯一实现了基于本地包的PEP 582的工具(PyFlow除外),这是一种替代虚拟环境的方式。值得注意的是,这个PEP最近被拒绝了。

Python版本管理:❌

包管理:✅

环境管理:✅

构建包:✅

发布包:✅

该工具是否管理依赖?✅

是否解析和锁定依赖?✅

构建和发布流程是否清晰?✅

是否支持插件?✅

是否支持PEP 660?✅

是否支持PEP 621?✅

pdm的主要命令与poetry类似。但目前还没有那么丰富,比如没有pdm shell或pdm new。

创建新项目:

# 交互式创建pyproject.toml

pdm init

# 从pyproject.toml安装包

pdm install

依赖管理:

# 添加依赖

pdm add <package_name>

# 显示所有依赖

pdm list --graph

运行代码:

# 没有shell命令

# 使用当前环境运行

pdm run python <script_name.py>

锁定文件:pdm的锁定功能与poetry类似。首次安装包时,pdm会解析pyproject.toml中列出的所有依赖并下载最新版本。安装完成后,pdm会将所有包及其下载的确切版本写入pdm.lock文件,将项目锁定到这些特定版本。建议将锁定文件提交到项目仓库,这样所有参与项目的人都使用相同版本的依赖。要将依赖更新到最新版本,请运行:

pdm update

构建/发布流程:

# 打包(创建.tar.gz和.whl)

pdm build

# 发布到PyPI

pdm publish

hatch

hatch(hatch.pypa.io/latest/):

Python版本管理:❌

包管理:❌

环境管理:✅

构建包:✅

发布包:✅

该工具是否管理依赖?❌

是否解析和锁定依赖?❌

构建和发布流程是否清晰?✅

是否支持插件?✅

是否支持PEP 660?✅

是否支持PEP 621?✅

值得一提的是,hatch的作者承诺很快会加入锁定功能。当你读到这篇文章时,请务必查看hatch的最新版本,看看这个功能是否已经实现。

创建新项目:

# 创建目录结构和pyproject.toml

hatch new <project_name>

# 交互式创建项目

hatch new -i <project_name>

# 初始化现有项目或创建pyproject.toml

hatch new --init

依赖管理:

# 没有add命令,依赖需手动添加到pyproject.toml

# 显示依赖

hatch dep show table

运行代码:

# 激活虚拟环境

hatch shell

# 在虚拟环境中运行脚本

hatch run python <script_name.py>

构建/发布流程:

# 打包(创建.tar.gz和.whl)

hatch build

# 发布到PyPI

hatch publish

声明式环境管理:hatch的一个特色是允许在pyproject.toml中配置虚拟环境。此外,它还允许专门为环境定义脚本。

rye

rye最近由Flask框架的创建者Armin Ronacher开发(2023年5月首发)。它深受编程语言Rust的打包工具rustup和cargo启发。

Python版本管理:✅

包管理:✅

环境管理:✅

构建包:✅

发布包:✅

该工具是否管理依赖?✅

是否解析和锁定依赖?✅

构建和发布流程是否清晰?✅

是否支持插件?❌

是否支持PEP 660?✅

是否支持PEP 621?✅

目前,rye没有插件接口。但由于开发活跃,将来可能会加上这个功能。

创建新项目:

# 创建目录结构和pyproject.toml

rye init <project_name>

# 指定Python版本

rye pin 3.10

依赖管理:

# 添加依赖,但不安装

rye add <package_name>

# 同步虚拟环境,锁定文件等

# 在这一步安装依赖

rye sync

运行代码:

# 激活虚拟环境

rye shell

# 在虚拟环境中运行脚本

rye run python <script_name.py>

构建/发布流程:

# 打包(创建.tar.gz和.whl)

rye build

# 发布到PyPI

rye publish

总结

在Python生态中,各种打包工具百花齐放,给开发者提供了丰富的选择。无论你是初学者还是有经验的Pythonista,我相信这篇全面细致的对比能帮你找到心仪的工具。以下是一张汇总表,直观展示了不同工具的功能评估:

工具

是否管理依赖

是否解析和锁定依赖

构建和发布流程是否清晰

是否支持插件

是否支持PEP 660

是否支持PEP 621

flit

poetry

pdm

hatch

rye

希望这篇深度长文让你对Python打包工具的复杂世界有了清晰的认识。无论你是刚接触Python打包,还是希望改进现有工作流,相信你都能从中获益。欢迎在评论区分享你的想法和使用体验。

(本内容为作者—晴天AI独立观点,未经允许不得转载,授权事宜请联系作者)——AI创造未来,科技改变生活!

查看全文
大家还看了
也许喜欢
更多游戏

Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved