今天我想介绍一个新的 Python 包管理工具:uv。它是一个采用 Rust 写的包管理器,旨在提供更快、更可靠的包管理体验:
- ⚡️ 10-100x faster than pip.
- 🚀 A single tool to replace pip, pip-tools, pipx, poetry, pyenv, twine, virtualenv, and more.
说实话,作为一个不是靠 Python 吃饭的程序员,每次写 Python 我的心情都挺复杂的,一方面是因为 Python 的语法和生态都很棒,我能够很快写出满足需求的脚本;另一方面是因为 Python 的包管理工具实在太糟糕了,每次稍微大型的 Python 项目,我都要去搜索一下最佳实践是什么,该用哪个 lsp、formatter,以及怎么管理虚拟环境难等一堆问题。
uv 的出现让我看到了希望,它的设计理念是简化 Python 的包管理流程,提供一个统一的工具来处理所有的包管理任务。
管理多个 Python 版本
Python 的演进很奇怪,每个版本之间的差异都很大,导致很多时候需要特定版本的 Python 才能运行某些包或项目。因此多版本管理是个刚需,通过 uv,我们可以通过简单的命令来安装和切换不同的 Python 版本。
|
|
管理虚拟环境
为了保证项目的依赖隔离,Python 社区推荐使用虚拟环境来管理项目的依赖。在 uv 之前,我们需要手动创建并激活,下面是一个典型的工作流:
|
|
uv 的理念与此不同,它默认就会为每个项目创建一个虚拟环境,并且在安装依赖时自动激活这个虚拟环境。这样我们就不需要手动管理虚拟环境了。
项目结构
虚拟环境往往是和一个项目绑定的, uv init
就会创建一个如下目录结构的项目:
. ├──.gitignore ├──.python-version ├──README.md ├──main.py ├──pyproject.toml
在我们后续执行 uv add
命令安装依赖时,uv 会自动在当前目录下创建一个虚拟环境,并且后续所有 uv 的命令(比如 uv run
uv sync
)都会使用这个虚拟环境。这样我们就可以直接使用 uv 来管理项目的依赖,而不需要手动创建和激活虚拟环境。一个完整的项目是这样的:
. ├── .venv │ ├── bin │ ├── lib │ └── pyvenv.cfg ├── .python-version ├── README.md ├── main.py ├── pyproject.toml └── uv.lock
pyproject.toml
pyproject.toml 是 Python 的标准配置文件,用于描述项目的元数据和依赖关系。uv 会自动生成这个文件,并且在我们添加依赖时会更新它:
|
|
与 uv 相关的配置项会被放在 [tool.uv]
部分,一般情况下不需要配置。更多可参考:Configuration files | uv。
uv.lock
uv.lock 是 uv 的锁文件,用于记录当前项目的依赖关系和版本信息。它类似于 npm 的 package-lock.json 或者 yarn.lock 文件。uv.lock 文件会在我们添加或更新依赖时自动生成和更新。
管理依赖
uv 提供了一个统一的命令来管理依赖,类似于 npm 的 install 命令。我们可以使用 uv add
命令来添加依赖,或者使用 uv remove
命令来删除依赖,uv 会自动更新 pyproject.toml 和 uv.lock 文件。
|
|
如果之前项目有 requirements.txt 文件,可以通过下面命令导入 pyproject.toml:
|
|
如果手动修改了 pyproject.toml 文件,uv 会自动检测到变更并更新 uv.lock 文件。我们也可以使用 uv sync
命令来同步依赖,这会根据 pyproject.toml 文件安装或更新依赖。更多可以参考:Managing dependencies。
运行项目
uv 提供了一个统一的命令来运行项目,类似于 npm 的 start 命令。我们可以使用 uv run
命令来运行项目,uv 会自动激活虚拟环境并执行指定的 Python 文件。
|
|
我们也可以使用 uv run
命令来运行任意的 Python 文件,比如:
|
|
|
|
当然,我们也可以手动激活虚拟环境,然后运行 Python 文件,这和之前的方式是一样的:
|
|
执行脚本
在 PEP 723 中定义了内联脚本元信息(inline script metadata),我们可以直接通过在一个 Python 文件中添加特殊注释来定义脚本的元信息,比如:
|
|
然后我们可以使用 uv run
命令来运行这个脚本,uv 会自动创建虚拟环境、安装依赖并执行脚本。
我们可以通过如下命令来向一个脚本添加依赖:
|
|
最后,我们可以将这个脚本打包成一个可执行文件,方便分发和运行:
|
|
假设这个脚本名为: greet
,并且有执行权限,我们可以直接运行它:
|
|
tool
许多 Python 包提供了可以作为工具使用的应用程序。uv 具有专门的支持,便于调用和安装工具。比如:
|
|
uv 在自动创建一个隔离的虚拟环境来运行工具,这样就可以避免工具之间的冲突。
如果一个工具经常使用,最好将其安装到持久环境中,并将其添加到 PATH 中( ~/.local/bin
),而不是反复调用 uvx。
我们可以使用 uv tool install
命令来安装工具,或者使用 uv tool uninstall
命令来删除工具。与 pip install
不同的是,安装工具并不会使其模块在当前环境中可用。例如,以下命令将失败:
|
|
这种隔离对于减少工具、脚本和项目依赖关系之间的相互作用和冲突至关重要。
总结
毫无疑问,uv 是一个非常有前途的 Python 包管理工具,它的设计理念和功能都很符合现代开发的需求。它简化了 Python 的包管理流程,提供了一个统一的工具来处理所有的包管理任务。
再加上 Rust 的性能优势,uv 在速度和可靠性上都表现得非常出色。对于所有需要写 Python 的开发者来说,uv 是一个值得尝试的工具。