—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