uv 是一个用 Rust 编写的极速 Python 包安装器和解析器,由 Astral 团队开发(也是 Ruff 的作者)。它旨在替代传统的 pip + venv + pip-tools 组合,同时提供类似 pipx 的全局工具管理功能。

uv 提供与 pip 高度兼容的接口,但不是 pip 的等价实现,通过底层优化实现了数倍到数十倍的速度提升。

uv = Python 版本管理 + 虚拟环境管理 + 包管理 + 项目依赖管理 + 工具安装

安装 uv

使用下面的命令可以安装 uv

1
2
3
4
5
# Linux/MacOS
curl -LsSf https://astral.sh/uv/install.sh | sh

# Windows
irm https://astral.sh/uv/install.ps1 | iex

安装后,uv 的可执行文件(uv,uvx)会被存放到 ~/.local/bin/uv,可以在任何目录下直接使用。

基本使用

uv 提供了与 pip 几乎完全相同的子命令,只需将 pip 替换为 uv pip 即可:

1
2
3
4
5
uv pip install requests             # 安装包
uv pip list # 列出已安装包

uv pip install -r requirements.txt # 批量安装依赖
uv pip freeze > requirements.txt # 批量导出依赖

同时 uv 也提供了更高级的原生命令,例如:

1
2
3
4
uv add requests                   # 添加依赖并更新锁文件
uv remove requests # 移除依赖
uv lock # 生成/更新 uv.lock 锁文件
uv sync # 根据锁文件同步环境

下面的命令会列出 uv 找到的或者可下载的 Python 版本

1
uv python list

与 conda 全局化的“重装环境”不同,uv 的设计思路基本延续了 venv 的项目级“轻量级虚拟环境”。
具体来说:

  • uv 不会在全局维护 Python 环境,它只会在当前项目中创建虚拟环境。(即使是 uv 管理的 Python 运行时也只是为各种虚拟环境提供支持的)
  • uv 管理第三方依赖的相关操作都是在项目的虚拟环境或者由 uv 临时提供的虚拟环境中进行的,并不会对全局的 Python 环境造成影响,除非显式加上对应的选项。

除此之外, uv 还为一些 Python cli 工具提供了独立的虚拟环境支持,用户对这些虚拟环境可以说是完全无感的。

uv 对可执行文件的处理也比较保守:uv 不会修改或覆盖 ~/.local/bin 目录中未由 uv 安装并管理的可执行文件。

Python 管理

uv 在执行命令或创建虚拟环境时会使用哪个 Python 运行时?

  • 如果 uv 在全局找到了 Python 运行时,并且满足版本要求,就直接使用它来创建虚拟环境;
  • 否则 uv 会尝试自行下载并管理对应版本的 Python 运行时;(uv 会优先使用自己管理的 Python 运行时)
  • 可以通过额外选项来控制选择使用 Python 运行时的策略。

uv 提供了一套 Python 运行时管理机制,例如:

  • 可以使用 uv python list 查看当前可用的 Python 运行时;
  • 可以使用 uv python install 3.13 下载指定版本的 Python 运行时;

默认情况下,uv 自己管理的 Python 运行时不会被暴露出来直接使用,而是在创建虚拟环境时使用。

由于 Windows 系统没有自带 Python 运行时,但是很多命令需要直接使用 pythonpython3 调用,可以使用 conda 的 base 作为全局环境,也可以在仅使用 uv 的情况下,用下面的命令将其暴露到 ~/.local/bin,从而可以直接使用对应的 pythonpython3 命令

1
uv python install 3.14 --default --preview

如果当前系统中有很多 Python 运行时,可以用下面的命令查询 uv 会优先使用哪一个:

1
2
3
4
5
uv python find

uv python find '>=3.12'

uv python find 3.12

运行脚本

无依赖脚本

对于无依赖的脚本,直接

1
uv run myscript.py

相当于

1
uv run python myscript.py

如果 uv 使用的和 python 命令对应的是同一个 Python 运行时,那么这近似等价于

1
python myscript.py

带依赖脚本

uv 还支持运行带有依赖的脚本,例如

example.py
1
2
3
4
5
import time
from rich.progress import track

for i in track(range(20), description="For example:"):
time.sleep(0.05)

此时直接运行会失败(如果当前 Python 环境没有 rich 包)

1
uv run example.py

但是可以使用如下命令指定依赖来运行

1
uv run --with rich example.py

此时 uv 会为其准备一个临时的虚拟环境来下载依赖并运行脚本,这个做法尤其适合用来临时测试一个脚本。

注意:在处理含依赖脚本时,不能处于一个 Python 项目中(含有 pyproject.toml 文件的目录),否则 uv 会进入项目模式,在项目的虚拟环境中处理依赖,可以使用 --no-project 选项抑制这种自动行为。

Python 最新版还提出了直接在 python 脚本头部元数据中声明版本和依赖的语法,例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "requests<3",
# "rich",
# ]
# ///

import requests
from rich.pretty import pprint

resp = requests.get("https://peps.python.org/api/peps.json")
data = resp.json()
pprint([(k, v["title"]) for k, v in data.items()][:10])

uv 也支持这种依赖语法,直接运行这个脚本即可。这个写法的优先级很高,即使在 Python 项目中,uv 也会视作单独的脚本来处理。

注意,在 Python 头部的元数据中,即使 dependencies 字段为空,也必须提供。

初始化脚本

uv 还提供了一个初始化单个 Python 脚本的语法,可以顺便声明版本和依赖,例如

1
uv init --script example.py --python 3.14

会自动在当前目录下创建一个 example.py 文件,并写入以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
# /// script
# requires-python = ">=3.14"
# dependencies = []
# ///


def main() -> None:
print("Hello from example.py!")


if __name__ == "__main__":
main()

使用工具

uv 提供了 uv tool 子命令,用于安装和运行全局 Python 工具,功能与 pipx 类似。

有两种模式:

  • 临时使用工具:uv 给这个工具准备一个临时性的单独虚拟环境并解决依赖关系,然后运行工具;(这些虚拟环境被视作缓存,但是缓存并不会立刻删除,通常可以复用)
  • 安装使用工具:与临时使用不同,uv 准备的单独虚拟环境是持续性的,但是仍然是专门提供给这个工具使用的。

临时使用工具

在这种使用情景下,uvx = uv tool run

例如直接运行 Ruff

1
2
3
uvx ruff
# 相当于
uv tool run ruff

uvx 会尽量尝试使用最新版本,除非指定版本。(由于涉及到缓存,实际使用的版本可能有所差异)

安装使用工具

uv 支持把 Python cli 工具安装使用

1
uv tool install ruff

此时不需要 uvx ruff,可以直接使用 ruff

实际上,uv 会把工具安装在 uv 管理的隔离目录(如 ~/.local/share/uv/tools~\AppData\Roaming\uv\tools),然后通过符号链接暴露可执行文件到 PATH,通常是存放在 .local/bin

可以指定 Github 仓库进行安装(这个例子的包名称就是 httpie)

1
2
3
uv tool install --from git+https://github.com/httpie/cli httpie

uv tool install git+ssh://git@github.com/httpie/cli

列出已安装的工具(安装以包为单位,一个包可能有多个 cli 工具)

1
uv tool list

升级

1
2
3
uv tool upgrade ruff

uv tool upgrade --all

卸载

1
uv tool uninstall ruff

项目开发

准备

一些 Python 项目中常见的文件说明:

  • .python-version 放在项目根目录中,指定 Python 版本,内容只有版本号数字,例如 3.12
  • py.typed:放在模块目录中,内容为空,是一个标记文件,用于向类型检查工具(如 mypy、Pyright)声明:这个 Python 包包含了内嵌的类型提示信息
  • uv.lock:uv 使用的通用的跨平台锁定文件,比 pyproject.toml 更详细。这个文件是可读的,但是通常不需要手动修改。uv.lock 需要被纳入 Git 版本控制,确保环境被精确复现。

可以使用下面的命令手动创建 .python-version 文件

1
uv python pin

初始化项目

uv 支持两种基本模板:应用程序和库,默认是应用程序,可以加上 --lib 选项来创建库项目。

uv 初始化时甚至还自动初始化了一个 git 仓库。

创建应用程序项目

1
2
3
4
uv init example-app

# 等于
uv init example-app --app

初始化得到的项目结构如下

1
2
3
4
./example-app
├── main.py
├── pyproject.toml
└── README.md

这个项目结构很简单,仅仅比单文件项目复杂一点,不适合打包发布,可以加上 --package 选项来创建支持打包的应用程序项目。

1
uv init example-app --package

初始化得到的项目结构如下

1
2
3
4
5
./example-app
├── pyproject.toml
├── README.md
└── src
└── example_app

创建库项目

1
uv init example-lib --lib

初始化得到项目结构如下

1
2
3
4
5
6
./example-lib
├── pyproject.toml
├── README.md
└── src
└── example_lib
└── py.typed

管理依赖

在项目中,可以直接使用 uv add 以及相关命令来添加依赖。

例如添加 requests

1
uv add requests

该命令会执行以下操作:

  • 如果项目中没有虚拟环境,会自动在 .venv 目录创建一个,并将依赖安装到虚拟环境中。(但是不会在当前 shell 中激活虚拟环境)
  • 解析 requests 及其依赖的最新兼容版本并下载依赖。
  • 更新 pyproject.toml 中的 dependencies 列表,添加 requests 及其版本约束(例如 requests>=2.32.0)。
  • 生成/更新 uv.lock 锁文件,记录所有依赖的精确版本和哈希值。

在 uv 的标准工作流程中,通常不需要在 shell 中显式激活虚拟环境,uv 会自动进入项目的虚拟环境进行处理。

也可以加上 --dev 选项来添加开发依赖,例如

1
uv add requests --dev

还可以直接从 requirements.txt 文件中批量添加依赖

1
uv add -r requirements.txt

移除依赖

1
uv remove requests

可以使用 uv tree 通过读取 uv.lock 文件来查看项目依赖关系树。

pip -e . 类似,uv 会自动以可编辑的形式将当前项目安装到虚拟环境中。

运行命令

编写代码后,可以直接 uv run xxx.py 运行对应脚本

1
2
3
4
uv run main.py

# or
uv run python main.py

如果项目提供了 cli,也可以运行

1
uv run example-cli

uv run 会确保当前项目环境与依赖一致(必要时安装或更新),然后在该环境中执行命令,不需要在之前手动激活虚拟环境。

如果要绕过 uv 直接运行脚本,则需要手动激活虚拟环境,再运行脚本

1
2
3
4
uv sync
source .venv/bin/activate

python main.py

项目版本控制

对于项目的版本信息控制,uv 也提供相应的命令,例如

1
2
uv version          # 查看版本
uv version 0.2.0 # 设置版本

还可以自动增加版本号

1
2
3
uv version --bump patch   # 0.1.0 -> 0.1.1
uv version --bump minor # 0.1.0 -> 0.2.0
uv version --bump major # 0.1.0 -> 1.0.0

uv 会自动维护 pyproject.toml 的版本信息。

锁定与同步

如果完全通过 uv adduv remove 等命令进行依赖修改,uv 通常会自动处理三个环节之间的同步:

  • pyproject.toml
  • uv.lock
  • .venv 虚拟环境

但是如果绕过 uv 直接修改其中的某个环节,例如手动修改 pyproject.toml 的情况,此时最好手动维护各个配置文件以及虚拟环境的一致性,这涉及到两个命令:uv lockuv sync

第一个命令是 uv lock,它会解析 pyproject.toml 并以此生成或更新 uv.lock 文件,但是不会修改虚拟环境。

1
uv lock

第二个命令是 uv sync,它会根据 uv.lock 更新当前项目的虚拟环境,确保虚拟环境中的包与锁文件完全一致,可能卸载掉一些包,或者安装一些包。

1
uv sync

uv sync 如果遇到 uv.lock 文件不存在,则会自动执行 uv lock 生成。

uv 支持指定镜像源,例如

1
uv sync --index-url https://mirrors.cloud.tencent.com/pypi/simple

虚拟环境管理

uv 内置了虚拟环境创建功能,并且在 uv 命令中通常不需要手动管理虚拟环境,但是也可以提供了对应的命令

1
2
uv venv                           # 在当前目录创建 .venv 虚拟环境
uv venv myenv # 指定名称,还可以指定 Python 版本

创建的虚拟环境结构和 venv 基本兼容,激活虚拟环境的方式也与传统的 venv 相同:

1
2
3
4
5
# Linux/macOS (bash)
source .venv/bin/activate

# Windows (pwsh)
.venv\Scripts\Activate.ps1

退出环境同样使用 deactivate

需要注意的是:

  • uv 创建的虚拟环境和 venv 创建的虚拟环境并不一样,例如默认不会自带 pip
  • uv venv 加上 --seed 选项可以创建一个自带 pip 的虚拟环境;
  • 推荐完全使用 uv 命令来负责虚拟环境的包管理,而非手动安装 pip 并混用。

其他使用情景

使用 uv 直接运行已有项目

当从外部获取一个 Python 项目时(无论其是否使用 uv),可以直接使用 uv 运行,而无需手动创建虚拟环境或安装依赖。

1
uv run main.py

此时 uv 会自动执行一系列行为:

  • 若项目包含 pyproject.toml,uv 会进入项目模式
    • 自动创建虚拟环境 .venv
    • 根据 uv.lockpyproject.toml 安装依赖
    • 然后运行
  • 若项目不包含 pyproject.toml,uv 会按脚本模式处理
    • 如果不含依赖,直接运行
    • 如果包含依赖选项或依赖元信息,为其配置临时环境,然后运行

对于项目模式,也可以单独进行项目的环境安装而不运行

1
uv sync

非项目型虚拟环境

在某些情况下(如数据分析、Jupyter),我们并不需要一个完整的 Python 项目结构,也并不关注依赖的具体版本,仅需要一个可以使用的环境。(类似 conda 环境)

可以手动创建虚拟环境:

1
2
3
uv venv .venv

uv pip install numpy matplotlib jupyterlab

此时绕过了项目管理,不需要 pyproject.tomluv.lock,uv 仅作为 venv + pip 的替代。

如果需要使用 pip 或兼容传统工具链,可以在创建虚拟环境时使用:

1
uv venv .venv --seed

此时最好激活虚拟环境后使用。

使用 uv 编写 Python 的 C/C++ 扩展

uv init 还提供了 Python 的 C/C++ 扩展的示例项目,可以直接从示例项目开始编写。

1
2
uv init example-extension --lib --build-backend scikit
cd example-extension/

注意 uv 所使用的 Python 版本,如果是 Linux 系统自带的 Python,可能会导致后面的 CMake 编译失败,因为缺少一些开发所需的组件,需要额外下载,使用 uv 自行管理的 Python 则不会遇到这个问题。

示例项目结构如下

1
2
3
4
5
6
7
8
9
10
11
.
├── CMakeLists.txt
├── pyproject.toml
├── README.md
├── src
│ ├── example_extension
│ │ ├── __init__.py
│ │ ├── _core.pyi
│ │ └── py.typed
│ └── main.cpp
└── uv.lock

直接 uv sync 即可,在这个过程中会自动触发 CMake 构建。

这个示例项目是一个 Python 库,可以用下面的命令来测试

1
uv run python -c "import example_extension; print(example_extension.hello())"

输出形如

1
Hello from example-extension!