Python uv 使用笔记
uv 是一个用 Rust 编写的极速 Python 包安装器和解析器,由 Astral 团队开发(也是 Ruff 的作者)。它旨在替代传统的 pip + venv + pip-tools 组合,同时提供类似 pipx 的全局工具管理功能。
uv 提供与 pip 高度兼容的接口,但不是 pip 的等价实现,通过底层优化实现了数倍到数十倍的速度提升。
uv = Python 版本管理 + 虚拟环境管理 + 包管理 + 项目依赖管理 + 工具安装
安装 uv
使用下面的命令可以安装 uv
1 | # Linux/MacOS |
安装后,uv 的可执行文件(uv,uvx)会被存放到 ~/.local/bin/uv,可以在任何目录下直接使用。
基本使用
uv 提供了与 pip 几乎完全相同的子命令,只需将 pip 替换为 uv pip 即可:
1 | uv pip install requests # 安装包 |
同时 uv 也提供了更高级的原生命令,例如:
1 | uv add requests # 添加依赖并更新锁文件 |
下面的命令会列出 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 运行时,但是很多命令需要直接使用 python 或 python3 调用,可以使用 conda 的 base 作为全局环境,也可以在仅使用 uv 的情况下,用下面的命令将其暴露到 ~/.local/bin,从而可以直接使用对应的 python 和 python3 命令
1 | uv python install 3.14 --default --preview |
如果当前系统中有很多 Python 运行时,可以用下面的命令查询 uv 会优先使用哪一个:
1 | uv python find |
运行脚本
无依赖脚本
对于无依赖的脚本,直接
1 | uv run myscript.py |
相当于
1 | uv run python myscript.py |
如果 uv 使用的和 python 命令对应的是同一个 Python 运行时,那么这近似等价于
1 | python myscript.py |
带依赖脚本
uv 还支持运行带有依赖的脚本,例如
1 | import time |
此时直接运行会失败(如果当前 Python 环境没有 rich 包)
1 | uv run example.py |
但是可以使用如下命令指定依赖来运行
1 | uv run --with rich example.py |
此时 uv 会为其准备一个临时的虚拟环境来下载依赖并运行脚本,这个做法尤其适合用来临时测试一个脚本。
注意:在处理含依赖脚本时,不能处于一个 Python 项目中(含有 pyproject.toml 文件的目录),否则 uv 会进入项目模式,在项目的虚拟环境中处理依赖,可以使用 --no-project 选项抑制这种自动行为。
Python 最新版还提出了直接在 python 脚本头部元数据中声明版本和依赖的语法,例如
1 | # /// script |
uv 也支持这种依赖语法,直接运行这个脚本即可。这个写法的优先级很高,即使在 Python 项目中,uv 也会视作单独的脚本来处理。
注意,在 Python 头部的元数据中,即使 dependencies 字段为空,也必须提供。
初始化脚本
uv 还提供了一个初始化单个 Python 脚本的语法,可以顺便声明版本和依赖,例如
1 | uv init --script example.py --python 3.14 |
会自动在当前目录下创建一个 example.py 文件,并写入以下内容:
1 | # /// script |
使用工具
uv 提供了 uv tool 子命令,用于安装和运行全局 Python 工具,功能与 pipx 类似。
有两种模式:
- 临时使用工具:uv 给这个工具准备一个临时性的单独虚拟环境并解决依赖关系,然后运行工具;(这些虚拟环境被视作缓存,但是缓存并不会立刻删除,通常可以复用)
- 安装使用工具:与临时使用不同,uv 准备的单独虚拟环境是持续性的,但是仍然是专门提供给这个工具使用的。
临时使用工具
在这种使用情景下,
uvx = uv tool run。
例如直接运行 Ruff
1 | uvx 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 | uv tool install --from git+https://github.com/httpie/cli httpie |
列出已安装的工具(安装以包为单位,一个包可能有多个 cli 工具)
1 | uv tool list |
升级
1 | uv tool upgrade ruff |
卸载
1 | uv tool uninstall ruff |
项目开发
准备
一些 Python 项目中常见的文件说明:
.python-version放在项目根目录中,指定 Python 版本,内容只有版本号数字,例如3.12py.typed:放在模块目录中,内容为空,是一个标记文件,用于向类型检查工具(如 mypy、Pyright)声明:这个 Python 包包含了内嵌的类型提示信息uv.lock:uv 使用的通用的跨平台锁定文件,比pyproject.toml更详细。这个文件是可读的,但是通常不需要手动修改。uv.lock需要被纳入 Git 版本控制,确保环境被精确复现。
可以使用下面的命令手动创建 .python-version 文件
1 | uv python pin |
初始化项目
uv 支持两种基本模板:应用程序和库,默认是应用程序,可以加上 --lib 选项来创建库项目。
uv 初始化时甚至还自动初始化了一个 git 仓库。
创建应用程序项目
1 | uv init example-app |
初始化得到的项目结构如下
1 | ./example-app |
这个项目结构很简单,仅仅比单文件项目复杂一点,不适合打包发布,可以加上 --package 选项来创建支持打包的应用程序项目。
1 | uv init example-app --package |
初始化得到的项目结构如下
1 | ./example-app |
创建库项目
1 | uv init example-lib --lib |
初始化得到项目结构如下
1 | ./example-lib |
管理依赖
在项目中,可以直接使用 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 | uv run main.py |
如果项目提供了 cli,也可以运行
1 | uv run example-cli |
uv run会确保当前项目环境与依赖一致(必要时安装或更新),然后在该环境中执行命令,不需要在之前手动激活虚拟环境。
如果要绕过 uv 直接运行脚本,则需要手动激活虚拟环境,再运行脚本
1 | uv sync |
项目版本控制
对于项目的版本信息控制,uv 也提供相应的命令,例如
1 | uv version # 查看版本 |
还可以自动增加版本号
1 | uv version --bump patch # 0.1.0 -> 0.1.1 |
uv 会自动维护 pyproject.toml 的版本信息。
锁定与同步
如果完全通过 uv add 或 uv remove 等命令进行依赖修改,uv 通常会自动处理三个环节之间的同步:
pyproject.tomluv.lock.venv虚拟环境
但是如果绕过 uv 直接修改其中的某个环节,例如手动修改 pyproject.toml 的情况,此时最好手动维护各个配置文件以及虚拟环境的一致性,这涉及到两个命令:uv lock 和 uv 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 | uv venv # 在当前目录创建 .venv 虚拟环境 |
创建的虚拟环境结构和 venv 基本兼容,激活虚拟环境的方式也与传统的 venv 相同:
1 | # Linux/macOS (bash) |
退出环境同样使用 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.lock或pyproject.toml安装依赖 - 然后运行
- 自动创建虚拟环境
- 若项目不包含
pyproject.toml,uv 会按脚本模式处理- 如果不含依赖,直接运行
- 如果包含依赖选项或依赖元信息,为其配置临时环境,然后运行
对于项目模式,也可以单独进行项目的环境安装而不运行
1 | uv sync |
非项目型虚拟环境
在某些情况下(如数据分析、Jupyter),我们并不需要一个完整的 Python 项目结构,也并不关注依赖的具体版本,仅需要一个可以使用的环境。(类似 conda 环境)
可以手动创建虚拟环境:
1 | uv venv .venv |
此时绕过了项目管理,不需要 pyproject.toml 和 uv.lock,uv 仅作为 venv + pip 的替代。
如果需要使用 pip 或兼容传统工具链,可以在创建虚拟环境时使用:
1 | uv venv .venv --seed |
此时最好激活虚拟环境后使用。
使用 uv 编写 Python 的 C/C++ 扩展
uv init 还提供了 Python 的 C/C++ 扩展的示例项目,可以直接从示例项目开始编写。
1 | uv init example-extension --lib --build-backend scikit |
注意 uv 所使用的 Python 版本,如果是 Linux 系统自带的 Python,可能会导致后面的 CMake 编译失败,因为缺少一些开发所需的组件,需要额外下载,使用 uv 自行管理的 Python 则不会遇到这个问题。
示例项目结构如下
1 | . |
直接 uv sync 即可,在这个过程中会自动触发 CMake 构建。
这个示例项目是一个 Python 库,可以用下面的命令来测试
1 | uv run python -c "import example_extension; print(example_extension.hello())" |
输出形如
1 | Hello from example-extension! |
