中文资讯 掘金 AI Sun, 20 Sep 2026 正文 ok 3043 words

uv 深度指南:重新理解 Python 包管理、依赖解析与项目工程化

英文标题:uv 深入指南:重新理解 Python 的包管理、依赖解析与项目工程化

由 Astral 团队用 Rust 开发的 uv 正在超越“更快的 pip”这一单一标签,成长为涵盖 Python 版本管理、虚拟环境、依赖锁定、命令行工具与项目构建的统一工程化平台。uv 通过引入原生项目模型,结合 pyproject.toml 与支持跨平台/架构的 universal resolution(通用依赖解析)锁文件 uv.lock,彻底解决了传统 requirements.txt 表达力受限及依赖不确定的痛点。借助全局缓存与高并发架构,uv 极大加速了现代 Python 开发与 CI/CD 流程,正在重新定义 Python 的工程化范式。
打开原文相关 2 · 机会 0
media3.2 KB media589 B media85.6 KB media64.6 KB media35.4 KB media50.2 KB media29.7 KB media93.9 KB media56.8 KB media5.6 KB

中文

对于 Python 的开发者来说,uv 并不只是一个“更快的 pip”。它实际上正在重新定义 Python 项目的依赖管理、虚拟环境管理、Python 版本管理、命令行工具管理以及项目构建流程。

ChatGPT Image 2026年9月20日 17_31_22.png

一、为什么 Python 需要 uv?

Python 的生态非常成熟,但长期以来,项目工程化实际上是由多个工具拼接完成的:

text Python ├── pyenv / conda → Python 版本管理 ├── venv / virtualenv → 虚拟环境 ├── pip → 包安装 ├── pip-tools → 依赖锁定 ├── pipx → CLI 工具隔离 ├── Poetry / PDM → 项目依赖管理 ├── setuptools → 包构建 └── twine → 包发布

这种工具链并不是不能使用,而是存在明显的问题:

  1. 工具之间职责分散;
  2. 不同工具之间存在配置重复;
  3. Python 版本、虚拟环境和依赖之间缺乏统一管理;
  4. requirements.txt 对复杂项目的表达能力有限;
  5. pip 本身并不是完整的项目管理工具;
  6. 依赖解析和安装速度长期是 Python 生态中的痛点。

uv 的出现,核心就是试图把这些能力统一起来。

官方将 uv 定义为:

An extremely fast Python package and project manager, written in Rust.(一个用Rust编写的速度极快的Python包和项目管理工具。)

也就是说,uv 的定位并不是单纯替代 pip,而是一个完整的 Python package and project manager(Python 包与项目管理器)。官方甚至将它定位为可以覆盖 pip、pip-tools、pipx、poetry、pyenv、twine、virtualenv 等工具能力的统一工具。(GitHub)


二、uv 到底是什么?

uv 是由 Astral 开发的 Python 工具,使用 Rust 实现。

Astral 同时也是 Ruff、ty 等 Python 工具的开发团队。

Image

Image

Image

Image

Image

Image

如果从功能层次来看,可以把 uv 理解成下面这个结构:

text uv │ ┌─────────────────┼──────────────────┐ │ │ │ Python 管理 项目管理 工具管理 │ │ │ uv python install uv init uvx uv python pin uv add uv tool uv python list uv remove uv lock uv sync uv run uv build uv publish │ │ └────────────┬────┘ │ pip 接口 │ uv pip install uv pip compile uv pip sync uv pip check

因此,uv 最值得理解的不是某个命令,而是它背后的统一项目模型。


三、uv 与 pip 最大的区别

很多开发者第一次接触 uv,会把它理解成:

bash pip install requests

换成:

bash uv pip install requests

这样理解只能看到 uv 的一小部分。

uv 实际上存在两套接口:

bash uv pip ...

以及:

bash uv ...

前者是 pip-compatible interface(兼容 pip 工作流的接口)。

后者是 uv 原生的 project interface(项目管理接口)。

例如:

bash uv pip install requests

和:

bash uv add requests

虽然最终都可能安装 requests,但它们表达的语义完全不同。

3.1 uv pip

它更接近传统 Python:

bash uv venv

uv pip install fastapi uv pip install pydantic

uv pip freeze uv pip list uv pip check

适用于:

  • 传统项目;
  • 已经存在 requirements.txt;
  • Docker 构建;
  • 临时虚拟环境;
  • 需要兼容 pip 工作流的项目。

3.2 原生 uv 项目

推荐的新项目则应该使用:

bash uv init uv add fastapi uv add pydantic uv run python main.py

这里的重点变化是:

依赖不再只是“安装到当前环境”,而是成为项目声明的一部分。

最终依赖会进入:

toml pyproject.toml

同时生成:

text uv.lock

因此:

bash pip install

更像是:

修改一个环境。

而:

bash uv add

更像是:

修改一个项目。

这是理解 uv 的第一个关键。


四、uv 的核心项目模型

一个典型的 uv 项目:

text my-project/ ├── .git/ ├── .gitignore ├── .python-version ├── .venv/ ├── pyproject.toml ├── uv.lock ├── README.md └── src/ └── my_project/ └── init.py

其中几个文件分别承担不同职责。

文件 作用
pyproject.toml 项目元数据与依赖声明
uv.lock 精确依赖解析结果
.python-version 项目 Python 版本选择
.venv 项目虚拟环境
src/ 项目源代码

uv 官方推荐将 uv.lock 纳入版本控制,因为它保存的是项目依赖的精确解析结果。(Astral 文档)


五、创建第一个 uv 项目

直接:

bash uv init my-project

进入:

bash cd my-project

然后:

bash uv run python

uv 会自动处理项目环境。

也可以明确创建:

bash uv sync

此时通常会得到:

text my-project/ ├── .venv/ ├── pyproject.toml └── uv.lock

这和传统流程:

bash python -m venv .venv

.venv\Scripts\activate

pip install -r requirements.txt

相比,少了大量手工步骤。


六、pyproject.toml:uv 项目的核心配置

一个典型配置:

toml [project] name = "my-project" version = "0.1.0" description = "My Python project" requires-python = ">=3.12" dependencies = [ "fastapi>=0.115", "pydantic>=2.10", "httpx>=0.28", ]

然后执行:

bash uv sync

uv 会:

  1. 读取项目声明;
  2. 解析依赖;
  3. 生成或更新 uv.lock;
  4. 创建 .venv;
  5. 安装解析后的依赖。

七、uv add 为什么比 pip install 更重要?

传统项目:

bash pip install fastapi

安装成功后,你还需要:

bash pip freeze > requirements.txt

然后手工维护依赖。

uv:

bash uv add fastapi

会直接修改:

toml [project] dependencies = [ "fastapi>=..." ]

同时更新:

text uv.lock

并同步项目环境。

所以推荐的工作方式是:

bash uv add fastapi uv add sqlalchemy uv add pydantic

而不是:

bash uv pip install fastapi uv pip install sqlalchemy uv pip install pydantic

如果你正在维护一个标准 uv 项目,后者实际上是在绕开 uv 的项目模型。


八、uv.lock:uv 最核心的能力之一

如果说:

toml pyproject.toml

解决的是:

我需要什么?

那么:

text uv.lock

解决的是:

到底安装什么?

例如:

toml dependencies = [ "fastapi>=0.115", ]

并不意味着每次安装都一定得到同一个 FastAPI 版本。

因为 FastAPI 还会依赖:

text starlette pydantic typing-extensions ...

这些依赖还有自己的依赖。

最终形成:

text fastapi ├── starlette │ └── anyio ├── pydantic │ ├── annotated-types │ └── pydantic-core └── typing-extensions

uv resolver(依赖解析器)会把整个依赖图进行解析。

最终把结果保存到:

text uv.lock


九、uv.lock 与 requirements.txt 有什么区别?

传统:

text requirements.txt

可能是:

text fastapi==0.115.6 pydantic==2.10.5 starlette==0.41.3 ...

它更像一个安装清单。

而 uv.lock 是 uv 项目的解析结果数据库。

它可以表达:

  • 精确版本;
  • 依赖关系;
  • Python 条件;
  • 操作系统条件;
  • CPU 架构;
  • package source(包来源);
  • Git dependency(Git 依赖);
  • URL dependency;
  • extras;
  • dependency groups;
  • workspace 信息等。

更重要的是:

uv.lock 是 universal lockfile(通用锁文件)。

它能够在同一个锁文件中描述不同平台、架构和 Python 版本下的依赖解析结果。(Astral 文档)


十、什么是 Universal Resolution(通用依赖解析)?

这是 uv 非常重要、但很多入门文章不会讲清楚的地方。

假设你的项目支持:

toml requires-python = ">=3.10"

同时需要:

text Windows Linux macOS

某个依赖可能存在:

text Linux → A Windows → B macOS → C

传统 requirements 文件通常需要针对不同环境生成不同结果。

而 uv 的 universal resolution(通用解析)会在一个 lockfile 中表达这些条件。

例如概念上可能形成:

text package-x==1.0 package-x==2.0

因此:

text 一个项目 │ └── 一个 uv.lock │ ├── Linux ├── Windows ├── macOS ├── x86_64 ├── ARM64 └── 不同 Python 版本

都可以由同一个锁文件描述。

uv 官方将其称为 universal resolution(通用依赖解析)。(Astral 文档)


十一、uv 为什么这么快?

uv 的性能并不是简单因为:

Rust 比 Python 快。

这只是其中一部分。

它的速度优势来自多个层面。

11.1 Rust 实现

uv 的核心实现使用 Rust。

相比传统 Python 工具链,减少了解释器层面的开销。


11.2 高度并发

依赖下载、元数据获取、安装等过程可以充分利用并发。


11.3 全局缓存

uv 使用 aggressive caching(积极缓存)。

例如:

text Global Cache │ ┌──────┼──────┐ │ │ │ Project A Project B Project C │ │ │ └──────┼──────┘ │ Shared Packages

同一个依赖不需要在每个项目里重复下载。

官方文档特别强调,uv 的缓存目录最好与 Python 环境位于同一文件系统,否则无法充分利用链接机制,可能退化为复制操作。(Astral 文档)


十二、uv 的缓存机制

可以查看缓存目录:

bash uv cache dir

清理缓存:

bash uv cache clean

清理无用缓存:

bash uv cache prune

如果遇到:

text Failed to hardlink files Falling back to full copy

这类提示,本质上通常意味着:

text cache ↓ 无法进行 hardlink ↓ fallback 到 copy ↓ 性能下降

特别是在:

  • Windows;
  • Docker;
  • 跨文件系统;
  • 网络文件系统;

中更容易遇到。

因此,uv 的缓存并不是简单的“下载缓存”,而是其性能架构的一部分。


十三、uv run:推荐的项目运行方式

传统 Python:

bash source .venv/bin/activate

python main.py

uv:

bash uv run python main.py

FastAPI:

bash uv run uvicorn app.main:app

Pytest:

bash uv run pytest

Ruff:

bash uv run ruff check .

它的一个重要特点是:

uv run 会在运行命令前检查 lockfile 和项目环境是否需要更新。

因此:

bash uv run pytest

并不只是:

bash python pytest

而更接近:

text 检查 pyproject.toml ↓ 检查 uv.lock ↓ 检查 .venv ↓ 同步环境 ↓ 执行 pytest

官方文档明确说明,uv run 会确保命令运行在项目所需的锁定依赖环境中。(Astral 文档)


十四、uv sync 到底做什么?

bash uv sync

可以理解成:

根据项目声明和 lockfile,把项目环境同步到期望状态。

例如:

text pyproject.toml ↓ uv.lock ↓ uv sync ↓ .venv

因此:

bash uv add

解决:

修改项目依赖。

而:

bash uv sync

解决:

让环境符合项目依赖。


十五、uv lock、uv sync、uv run 的区别

这是实际开发中非常容易混淆的三个命令。

命令 核心职责
uv lock 更新依赖解析结果
uv sync 将环境同步到 lockfile
uv run 同步后执行命令

可以理解成:

text pyproject.toml │ │ uv lock ↓ uv.lock │ │ uv sync ↓ .venv │ │ uv run ↓ application


十六、--locked 和 --frozen

CI/CD 中非常重要。

--locked

bash uv run --locked pytest

含义是:

不允许更新 lockfile。

如果:

text pyproject.toml ≠ uv.lock

就直接失败。

适合 CI。


--frozen

bash uv run --frozen pytest

更严格:

直接把已有 uv.lock 当作唯一事实来源,不检查它是否需要更新。

如果没有 uv.lock,直接失败。

因此 CI 中经常可以看到类似:

bash uv sync --locked uv run --locked pytest

或者在完全受控的构建流程中使用 --frozen。

官方 CLI 对两者的语义有明确区分。(Astral 文档)


十七、开发依赖:dependency groups

传统项目通常:

text requirements.txt requirements-dev.txt

uv 推荐:

toml [dependency-groups] dev = [ "pytest", "ruff", "mypy", ]

然后:

bash uv add

等价于:

bash uv add --group dev pytest

也可以创建更多组:

toml [dependency-groups] dev = [ "pytest", ]

lint = [ "ruff", ]

docs = [ "mkdocs", ]

安装:

bash uv sync --group docs

全部:

bash uv sync --all-groups

uv 会对 dependency groups 一起进行依赖解析,因此不同 group 之间如果存在不可解决的版本冲突,lock 阶段就会失败。(Astral 文档)


十八、为什么 dependency groups 比 requirements-dev.txt 更好?

假设:

text requirements.txt req

English / 原文

对于 Python 的开发者来说,uv 并不只是一个“更快的 pip”。它实际上正在重新定义 Python 项目的依赖管理、虚拟环境管理、Python 版本管理、命令行工具管理以及项目构建流程。

ChatGPT Image 2026年9月20日 17_31_22.png

一、为什么 Python 需要 uv?

Python 的生态非常成熟,但长期以来,项目工程化实际上是由多个工具拼接完成的:

Python
 ├── pyenv / conda       → Python 版本管理
 ├── venv / virtualenv   → 虚拟环境
 ├── pip                 → 包安装
 ├── pip-tools           → 依赖锁定
 ├── pipx                → CLI 工具隔离
 ├── Poetry / PDM        → 项目依赖管理
 ├── setuptools          → 包构建
 └── twine               → 包发布

这种工具链并不是不能使用,而是存在明显的问题:

  1. 工具之间职责分散;
  2. 不同工具之间存在配置重复;
  3. Python 版本、虚拟环境和依赖之间缺乏统一管理;
  4. requirements.txt 对复杂项目的表达能力有限;
  5. pip 本身并不是完整的项目管理工具;
  6. 依赖解析和安装速度长期是 Python 生态中的痛点。

uv 的出现,核心就是试图把这些能力统一起来。

官方将 uv 定义为:

An extremely fast Python package and project manager, written in Rust.(一个用Rust编写的速度极快的Python包和项目管理工具。)

也就是说,uv 的定位并不是单纯替代 pip,而是一个完整的 Python package and project manager(Python 包与项目管理器) 。官方甚至将它定位为可以覆盖 pip、pip-tools、pipx、poetry、pyenv、twine、virtualenv 等工具能力的统一工具。(GitHub)


二、uv 到底是什么?

uv 是由 Astral 开发的 Python 工具,使用 Rust 实现。

Astral 同时也是 Ruff、ty 等 Python 工具的开发团队。

Image

Image

Image

Image

Image

Image

如果从功能层次来看,可以把 uv 理解成下面这个结构:

                         uv
                          │
        ┌─────────────────┼──────────────────┐
        │                 │                  │
   Python 管理        项目管理           工具管理
        │                 │                  │
 uv python install   uv init            uvx
 uv python pin       uv add             uv tool
 uv python list      uv remove
                     uv lock
                     uv sync
                     uv run
                     uv build
                     uv publish
        │                 │
        └────────────┬────┘
                     │
                 pip 接口
                     │
               uv pip install
               uv pip compile
               uv pip sync
               uv pip check

因此,uv 最值得理解的不是某个命令,而是它背后的统一项目模型 。


三、uv 与 pip 最大的区别

很多开发者第一次接触 uv,会把它理解成:

pip install requests

换成:

uv pip install requests

这样理解只能看到 uv 的一小部分。

uv 实际上存在两套接口:

uv pip ...

以及:

uv ...

前者是 pip-compatible interface(兼容 pip 工作流的接口) 。

后者是 uv 原生的 project interface(项目管理接口) 。

例如:

uv pip install requests

和:

uv add requests

虽然最终都可能安装 requests,但它们表达的语义完全不同。

3.1 uv pip

它更接近传统 Python:

uv venv

uv pip install fastapi
uv pip install pydantic

uv pip freeze
uv pip list
uv pip check

适用于:

  • 传统项目;
  • 已经存在 requirements.txt;
  • Docker 构建;
  • 临时虚拟环境;
  • 需要兼容 pip 工作流的项目。

3.2 原生 uv 项目

推荐的新项目则应该使用:

uv init
uv add fastapi
uv add pydantic
uv run python main.py

这里的重点变化是:

依赖不再只是“安装到当前环境”,而是成为项目声明的一部分。

最终依赖会进入:

pyproject.toml

同时生成:

uv.lock

因此:

pip install

更像是:

修改一个环境。

而:

uv add

更像是:

修改一个项目。

这是理解 uv 的第一个关键。


四、uv 的核心项目模型

一个典型的 uv 项目:

my-project/
├── .git/
├── .gitignore
├── .python-version
├── .venv/
├── pyproject.toml
├── uv.lock
├── README.md
└── src/
    └── my_project/
        └── __init__.py

其中几个文件分别承担不同职责。

文件 作用
pyproject.toml 项目元数据与依赖声明
uv.lock 精确依赖解析结果
.python-version 项目 Python 版本选择
.venv 项目虚拟环境
src/ 项目源代码

uv 官方推荐将 uv.lock 纳入版本控制,因为它保存的是项目依赖的精确解析结果。(Astral 文档)


五、创建第一个 uv 项目

直接:

uv init my-project

进入:

cd my-project

然后:

uv run python

uv 会自动处理项目环境。

也可以明确创建:

uv sync

此时通常会得到:

my-project/
├── .venv/
├── pyproject.toml
└── uv.lock

这和传统流程:

python -m venv .venv

.venv\Scripts\activate

pip install -r requirements.txt

相比,少了大量手工步骤。


六、pyproject.toml:uv 项目的核心配置

一个典型配置:

[project]
name = "my-project"
version = "0.1.0"
description = "My Python project"
requires-python = ">=3.12"
dependencies = [
    "fastapi>=0.115",
    "pydantic>=2.10",
    "httpx>=0.28",
]

然后执行:

uv sync

uv 会:

  1. 读取项目声明;
  2. 解析依赖;
  3. 生成或更新 uv.lock;
  4. 创建 .venv;
  5. 安装解析后的依赖。

七、uv add 为什么比 pip install 更重要?

传统项目:

pip install fastapi

安装成功后,你还需要:

pip freeze > requirements.txt

然后手工维护依赖。

uv:

uv add fastapi

会直接修改:

[project]
dependencies = [
    "fastapi>=..."
]

同时更新:

uv.lock

并同步项目环境。

所以推荐的工作方式是:

uv add fastapi
uv add sqlalchemy
uv add pydantic

而不是:

uv pip install fastapi
uv pip install sqlalchemy
uv pip install pydantic

如果你正在维护一个标准 uv 项目,后者实际上是在绕开 uv 的项目模型。


八、uv.lock:uv 最核心的能力之一

如果说:

pyproject.toml

解决的是:

我需要什么?

那么:

uv.lock

解决的是:

到底安装什么?

例如:

dependencies = [
    "fastapi>=0.115",
]

并不意味着每次安装都一定得到同一个 FastAPI 版本。

因为 FastAPI 还会依赖:

starlette
pydantic
typing-extensions
...

这些依赖还有自己的依赖。

最终形成:

fastapi
├── starlette
│   └── anyio
├── pydantic
│   ├── annotated-types
│   └── pydantic-core
└── typing-extensions

uv resolver(依赖解析器)会把整个依赖图进行解析。

最终把结果保存到:

uv.lock

九、uv.lock 与 requirements.txt 有什么区别?

传统:

requirements.txt

可能是:

fastapi==0.115.6
pydantic==2.10.5
starlette==0.41.3
...

它更像一个安装清单 。

而 uv.lock 是 uv 项目的解析结果数据库 。

它可以表达:

  • 精确版本;
  • 依赖关系;
  • Python 条件;
  • 操作系统条件;
  • CPU 架构;
  • package source(包来源);
  • Git dependency(Git 依赖);
  • URL dependency;
  • extras;
  • dependency groups;
  • workspace 信息等。

更重要的是:

uv.lock 是 universal lockfile(通用锁文件)。

它能够在同一个锁文件中描述不同平台、架构和 Python 版本下的依赖解析结果。(Astral 文档)


十、什么是 Universal Resolution(通用依赖解析)?

这是 uv 非常重要、但很多入门文章不会讲清楚的地方。

假设你的项目支持:

requires-python = ">=3.10"

同时需要:

Windows
Linux
macOS

某个依赖可能存在:

Linux → A
Windows → B
macOS → C

传统 requirements 文件通常需要针对不同环境生成不同结果。

而 uv 的 universal resolution(通用解析)会在一个 lockfile 中表达这些条件。

例如概念上可能形成:

package-x==1.0 
package-x==2.0 

因此:

一个项目
   │
   └── 一个 uv.lock
          │
          ├── Linux
          ├── Windows
          ├── macOS
          ├── x86_64
          ├── ARM64
          └── 不同 Python 版本

都可以由同一个锁文件描述。

uv 官方将其称为 universal resolution(通用依赖解析) 。(Astral 文档)


十一、uv 为什么这么快?

uv 的性能并不是简单因为:

Rust 比 Python 快。

这只是其中一部分。

它的速度优势来自多个层面。

11.1 Rust 实现

uv 的核心实现使用 Rust。

相比传统 Python 工具链,减少了解释器层面的开销。


11.2 高度并发

依赖下载、元数据获取、安装等过程可以充分利用并发。


11.3 全局缓存

uv 使用 aggressive caching(积极缓存)。

例如:

         Global Cache
              │
       ┌──────┼──────┐
       │      │      │
    Project A Project B Project C
       │      │      │
       └──────┼──────┘
              │
          Shared Packages

同一个依赖不需要在每个项目里重复下载。

官方文档特别强调,uv 的缓存目录最好与 Python 环境位于同一文件系统,否则无法充分利用链接机制,可能退化为复制操作。(Astral 文档)


十二、uv 的缓存机制

可以查看缓存目录:

uv cache dir

清理缓存:

uv cache clean

清理无用缓存:

uv cache prune

如果遇到:

Failed to hardlink files
Falling back to full copy

这类提示,本质上通常意味着:

cache
  ↓
无法进行 hardlink
  ↓
fallback 到 copy
  ↓
性能下降

特别是在:

  • Windows;
  • Docker;
  • 跨文件系统;
  • 网络文件系统;

中更容易遇到。

因此,uv 的缓存并不是简单的“下载缓存”,而是其性能架构的一部分。


十三、uv run:推荐的项目运行方式

传统 Python:

source .venv/bin/activate

python main.py

uv:

uv run python main.py

FastAPI:

uv run uvicorn app.main:app

Pytest:

uv run pytest

Ruff:

uv run ruff check .

它的一个重要特点是:

uv run 会在运行命令前检查 lockfile 和项目环境是否需要更新。

因此:

uv run pytest

并不只是:

python pytest

而更接近:

检查 pyproject.toml
        ↓
检查 uv.lock
        ↓
检查 .venv
        ↓
同步环境
        ↓
执行 pytest

官方文档明确说明,uv run 会确保命令运行在项目所需的锁定依赖环境中。(Astral 文档)


十四、uv sync 到底做什么?

uv sync

可以理解成:

根据项目声明和 lockfile,把项目环境同步到期望状态。

例如:

pyproject.toml
       ↓
    uv.lock
       ↓
     uv sync
       ↓
     .venv

因此:

uv add

解决:

修改项目依赖。

而:

uv sync

解决:

让环境符合项目依赖。


十五、uv lock、uv sync、uv run 的区别

这是实际开发中非常容易混淆的三个命令。

命令 核心职责
uv lock 更新依赖解析结果
uv sync 将环境同步到 lockfile
uv run 同步后执行命令

可以理解成:

pyproject.toml
       │
       │ uv lock
       ↓
   uv.lock
       │
       │ uv sync
       ↓
     .venv
       │
       │ uv run
       ↓
   application

十六、--locked 和 --frozen

CI/CD 中非常重要。

--locked

uv run --locked pytest

含义是:

不允许更新 lockfile。

如果:

pyproject.toml
      ≠
uv.lock

就直接失败。

适合 CI。


--frozen

uv run --frozen pytest

更严格:

直接把已有 uv.lock 当作唯一事实来源,不检查它是否需要更新。

如果没有 uv.lock,直接失败。

因此 CI 中经常可以看到类似:

uv sync --locked
uv run --locked pytest

或者在完全受控的构建流程中使用 --frozen。

官方 CLI 对两者的语义有明确区分。(Astral 文档)


十七、开发依赖:dependency groups

传统项目通常:

requirements.txt
requirements-dev.txt

uv 推荐:

[dependency-groups]
dev = [
    "pytest",
    "ruff",
    "mypy",
]

然后:

uv add 

等价于:

uv add --group dev pytest

也可以创建更多组:

[dependency-groups]
dev = [
    "pytest",
]

lint = [
    "ruff",
]

docs = [
    "mkdocs",
]

安装:

uv sync --group docs

全部:

uv sync --all-groups

uv 会对 dependency groups 一起进行依赖解析,因此不同 group 之间如果存在不可解决的版本冲突,lock 阶段就会失败。(Astral 文档)


十八、为什么 dependency groups 比 requirements-dev.txt 更好?

假设:

requirements.txt
requirements-dev.txt
requirements-test.txt
requirements-docs.txt

每个文件都可能存在重复依赖:

pytest
ruff
pydantic

而 uv:

[project]
dependencies = [
    "fastapi",
]

[dependency-groups]
dev = [
    "ruff",
]

test = [
    "pytest",
]

docs = [
    "mkdocs",
]

这些依赖最终可以进入同一个:

uv.lock

因此:

一个项目
   │
   ├── production dependencies
   ├── dev dependencies
   ├── test dependencies
   └── docs dependencies
             │
             ↓
          uv.lock

这是大型项目管理上的重要优势。


十九、可选依赖 extras

如果你的项目是一个需要发布的 Python package,可以使用:

[project.optional-dependencies]

postgres = [
    "asyncpg",
]

redis = [
    "redis",
]

all = [
    "asyncpg",
    "redis",
]

然后:

uv sync --extra postgres

或者:

uv sync --all-extras

这里需要区分:

dependency groups

和:

optional dependencies / extras

一般可以理解为:

dependency groups
→ 项目开发环境组织方式

extras
→ package 对外提供的可选功能

二十、Python 版本管理

这是 uv 与 pip 最大的能力差异之一。

uv 不仅管理 package,还可以管理 Python 本身。

例如:

uv python install 3.12

查看:

uv python list

安装多个版本:

uv python install 3.11 3.12 3.13

指定项目:

uv python pin 3.12

会生成:

.python-version

例如:

3.12

之后项目中的 uv 命令就可以根据这个版本工作。

官方当前支持管理 CPython、PyPy、Pyodide 等实现,并能够根据需要自动获取 Python。(GitHub)


二十一、.python-version 与 requires-python

这是两个经常被混淆的概念。

例如:

[project]
requires-python = ">=3.11,<3.14"

表达的是:

这个项目支持哪些 Python 版本。

而:

.python-version

表达的是:

当前开发环境默认使用哪个 Python 版本。

因此:

requires-python
    ↓
兼容范围

.python-version
    ↓
当前项目默认解释器

例如:

requires-python = ">=3.11,<3.14"

同时:

.python-version
3.12

完全合理。


二十二、uvx:Python CLI 工具的新方式

以前安装 Ruff:

pip install ruff

然后:

ruff check .

如果只是临时使用某个工具,就会产生一个问题:

为什么为了运行一个 CLI 工具,要把它安装到当前环境?

uv 提供:

uvx ruff

它实际上等价于:

uv tool run ruff

uv 会为工具创建隔离环境。

因此:

uvx ruff
uvx black
uvx mypy

不会污染你的项目环境。

官方建议,对于很多“一次性执行”的 Python CLI 工具,uvx 往往比永久安装更加合适。(GitHub)


二十三、uv tool install

如果你需要长期使用某个 CLI:

uv tool install ruff

它会安装到独立的工具环境,但将命令暴露到 PATH。

因此:

项目依赖
    ↓
  uv add

临时 CLI
    ↓
   uvx

长期 CLI
    ↓
uv tool install

这是非常清晰的职责划分。


二十四、uv 支持 PEP 723:单文件 Python 应用

uv 还有一个非常有意思的能力:

给一个 .py 文件声明自己的依赖。

例如:

import requests
from rich import print

print(requests.get("https://example.com").status_code)

然后:

uv run example.py

uv 会根据脚本中的 metadata(元数据)创建隔离环境并安装依赖。

这特别适合:

  • 运维脚本;
  • 数据处理脚本;
  • 自动化脚本;
  • 临时工具;
  • AI 实验代码。

官方文档将这一能力建立在 PEP 723 inline script metadata(内联脚本依赖元数据)之上。(Astral 文档)


二十五、uv pip:传统项目迁移的桥梁

如果你暂时不想修改现有项目:

requirements.txt

完全可以:

uv venv

uv pip install -r requirements.txt

甚至:

uv pip compile requirements.in

以及:

uv pip sync requirements.txt

因此 uv 并不要求你“一次性重构整个项目”。

可以采用:

旧项目
  │
  ├── pip
  ↓
uv pip
  │
  ↓
pyproject.toml
  │
  ↓
uv.lock
  │
  ↓
完整 uv project

官方也专门提供了从 pip 项目迁移到 uv project 的迁移路径。(Astral 文档)


二十六、uv pip compile

如果你的团队仍然依赖:

requirements.in

可以:

uv pip compile requirements.in \
    --output-file requirements.txt

它类似于:

pip-tools / pip-compile

但底层使用 uv 的 resolver(依赖解析器)。

还可以:

uv pip sync requirements.txt

将环境严格同步到 requirements 文件。

因此对于旧项目:

requirements.in
       ↓
uv pip compile
       ↓
requirements.txt
       ↓
uv pip sync
       ↓
.venv

是一个非常实用的迁移方案。(Astral 文档)


二十七、uv 的依赖解析能力

对于资深 Python 开发者来说,这可能比 CLI 更值得关注。

uv 的 resolver 不只是:

找到一个能安装的版本

而是对整个依赖图进行统一解析。

例如:

A
├── C >= 1,<3
│
B
└── C >= 2,<4

那么 resolver 需要找到:

C >=2,<3

如果:

A → C <2
B → C >=2

就无法解决。

uv 会报告:

No solution found

而不是简单地把某个版本装进去。


二十八、Dependency Overrides

真实项目中经常遇到:

A → C <2
B → C >=2

但你经过测试后发现:

A 实际上也可以使用 C 2.x。

这时候可以使用 dependency override(依赖覆盖)。

例如:

[tool.uv]
override-dependencies = [
    "C>=2",
]

但这应该谨慎使用。

因为:

override 本质上是在告诉 resolver:忽略原始包声明中的某些约束。

如果上游包真的不兼容新版本,那么:

resolver 成功
≠
程序运行正确

因此 override 应该被视为最后手段,而不是解决依赖冲突的常规方式。(Astral 文档)


二十九、Constraint 与 Override 的区别

这是高级依赖管理中非常重要的一组概念。

Constraint(约束)

constraint

是在缩小候选范围。

例如:

C>=2,<3

它不会主动把 C 加入项目,只是在 C 已经出现时限制版本。


Override

override

则是在告诉 resolver:

忽略原始依赖声明,按照我的规则解析。

所以:

constraint
→ 缩小范围

override
→ 改写约束

两者不要混用。


三十、Package Index:官方源、私有源与多源

企业项目通常不会只使用 PyPI。

例如:

PyPI
+
公司内部 PyPI
+
PyTorch index

uv 支持配置多个 package index(包索引)。

例如:

[[tool.uv.index]]
name = "pytorch"
url = "https://download.pytorch.org/whl/cpu"

然后:

[tool.uv.sources]
torch = { index = "pytorch" }

这样就可以让不同依赖从不同来源解析。

官方文档支持配置多个 index,并定义它们的优先顺序。(GitHub)

这对于:

  • PyTorch;
  • CUDA;
  • 企业私有包;
  • 内部 SDK;
  • 镜像仓库;

尤其重要。


三十一、Git 依赖

uv 支持直接:

uv add git+https:

也可以在:

[tool.uv.sources]
my-package = { git = "https://github.com/example/my-package" }

中定义。

这对于内部组件、尚未发布到 PyPI 的 package 很有价值。

同时 uv 会对 Git dependency(Git 依赖)的解析结果进行缓存,并能够在编译依赖时固定到具体 commit。(Astral 文档)


三十二、Path Dependency

大型 Python 项目经常存在:

project/
├── app/
└── libs/
    └── common/

可以使用:

[tool.uv.sources]
common = { path = "./libs/common" }

开发过程中还可以使用 editable:

[tool.uv.sources]
common = { path = "./libs/common", editable = true }

这样非常适合:

主应用
  ↓
内部 Python SDK
  ↓
公共工具库

这种多包开发模式。


三十三、Workspace:大型 Python 仓库的解决方案

如果项目越来越大:

repo/
├── services/
│   ├── api/
│   ├── worker/
│   └── scheduler/
│
└── libs/
    ├── common/
    └── storage/

你可能不希望每个 package 都完全独立。

uv 提供:

workspace

这个概念来自 Rust Cargo。

一个 workspace 可以:

package A
package B
package C
       │
       ↓
   一个 uv.lock

每个 package 有自己的:

pyproject.toml

但 workspace 共享:

uv.lock

因此可以统一管理大型 monorepo(单仓多项目)。(Astral 文档)


三十四、什么时候应该使用 Workspace?

适合:

Monorepo
大型 Python 平台
多个内部 SDK
多个相互依赖的 Python package

例如:

document-platform/
├── services/
│   ├── api/
│   └── parser/
│
├── packages/
│   ├── common/
│   ├── storage/
│   └── client/
│
└── pyproject.toml

可以将其组织成 workspace。

但是 workspace 并不是越早使用越好。

如果几个 package:

  • 需要完全独立的 Python 版本;
  • 依赖版本存在冲突;
  • 希望完全独立的虚拟环境;

那么 path dependency 可能更合适。

官方也明确指出,存在冲突需求或独立环境需求时,workspace 并不一定是最佳选择。(Astral 文档)


三十五、uv build

uv 不只是环境管理工具。

还可以:

uv build

生成:

dist/
├── xxx.tar.gz
└── xxx.whl

也就是:

sdist
wheel

然后:

uv publish

发布到 package index。

官方文档目前将 uv build 和 uv publish 都作为项目打包与发布能力的一部分。(GitHub)

因此:

开发
 ↓
uv add
 ↓
uv lock
 ↓
uv test
 ↓
uv build
 ↓
uv publish

已经可以形成比较完整的 Python package 生命周期。


三十六、uv 与 Poetry 的区别

很多开发者会问:

uv 是不是另一个 Poetry?

从项目管理角度看,两者确实存在明显重叠。

但理念不同。

可以粗略理解:

Poetry
→ Python project manager

uv
→ Python ecosystem manager(Python 生态系统管理器)

uv 更强调:

Python
+ package
+ project
+ environment
+ tools
+ scripts
+ workspace
+ build

并且提供:

uv pip

作为传统工作流的兼容入口。

因此 uv 的迁移成本通常比较低。


三十七、uv 与 conda 的区别

conda 不只是 Python package manager。

它实际上管理:

Python
C/C++
系统级依赖
二进制库
科学计算环境

因此:

conda

在科学计算领域仍然有自己的价值。

uv 的核心目标则更加聚焦:

Python runtime
+
Python packages
+
Python projects

对于:

FastAPI
Django
LangChain
AI Agent
Web backend
CLI
普通 Python 服务

uv 通常更加轻量。


三十八、uv 与 pyenv 的区别

传统:

pyenv
  ↓
管理 Python 版本

venv
  ↓
创建虚拟环境

pip
  ↓
安装 package

uv 可以把这些能力整合:

uv python install
       ↓
Python

uv venv
       ↓
Virtual Environment

uv add
       ↓
Dependencies

因此不再必须维护:

pyenv + venv + pip

这样一套工具组合。


三十九、uv 与 pipx 的区别

传统:

pipx install ruff

uv:

uv tool install ruff

临时运行:

uvx ruff

所以:

pipx
  ↓
隔离 CLI 工具

uv tool
  ↓
隔离 CLI 工具

uvx
  ↓
临时执行 CLI 工具

四十、uv 与 Docker

uv 非常适合现代 Python Docker 构建。

例如:

FROM python:3.12-slim

COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/

WORKDIR /app

COPY pyproject.toml uv.lock ./

RUN uv sync --locked --no-install-project

COPY . .

RUN uv sync --locked

CMD ["uv", "run", "python", "main.py"]

核心思想是:

先复制依赖描述
        ↓
uv sync
        ↓
Docker cache
        ↓
再复制源代码

这样修改 Python 源代码时,不必每次重新安装所有依赖。

对于 CI/CD,还可以进一步使用:

uv sync --locked

确保构建过程不会偷偷修改锁文件。


四十一、企业项目中推荐的目录结构

对于一个中大型 Python 服务,我更推荐:

my-service/
├── .github/
│   └── workflows/
├── src/
│   └── my_service/
│       ├── api/
│       ├── core/
│       ├── models/
│       ├── services/
│       └── main.py
├── tests/
├── scripts/
├── pyproject.toml
├── uv.lock
├── .python-version
├── .gitignore
└── README.md

pyproject.toml:

[project]
name = "my-service"
version = "0.1.0"
requires-python = ">=3.12,<3.14"
dependencies = [
    "fastapi",
    "pydantic",
    "sqlalchemy",
]

[dependency-groups]
dev = [
    "pytest",
    "ruff",
    "mypy",
]

[build-system]
requires = ["uv_build>=0.12,<0.13"]
build-backend = "uv_build"

然后:

uv sync

开发:

uv run pytest
uv run ruff check .
uv run mypy src

运行:

uv run python -m my_service

四十二、uv 常用命令速查

Python 管理

uv python list
uv python install 3.12
uv python pin 3.12
uv python find

项目

uv init
uv add fastapi
uv remove fastapi
uv sync
uv lock
uv run
uv tree

开发依赖

uv add 
uv add 

uv sync 
uv sync 

虚拟环境

uv venv
uv venv --python 3.12

pip 兼容接口

uv pip install
uv pip uninstall
uv pip list
uv pip freeze
uv pip check
uv pip compile
uv pip sync

工具

uvx ruff
uv tool install ruff
uv tool list
uv tool uninstall ruff

构建发布

uv build
uv publish

缓存

uv cache dir
uv cache clean
uv cache prune

四十三、传统项目迁移到 uv 的推荐路径

不要直接把大型生产项目一次性重构。

推荐分阶段。

第一阶段:只替换 pip

原来:

python -m venv .venv
pip install -r requirements.txt

改成:

uv venv
uv pip install -r requirements.txt

先验证:

应用
测试
CI
Docker

都没有问题。


第二阶段:引入 pyproject.toml

创建:

uv init

然后迁移:

uv add -r requirements.txt

将依赖转移到:

[project]
dependencies = [...]

第三阶段:使用 uv.lock

执行:

uv lock

提交:

uv.lock

到 Git。


第四阶段:统一开发命令

把:

python -m pytest
python -m uvicorn ...
ruff ...

逐渐统一为:

uv run pytest
uv run uvicorn ...
uv run ruff ...

第五阶段:CI/CD

将:

pip install ...

替换成:

uv sync --locked

这样 CI 环境和开发环境都基于:

pyproject.toml
+
uv.lock

四十四、生产项目应该提交哪些文件?

通常:

提交 Git:

pyproject.toml
uv.lock
.python-version

通常不提交:

.venv/

例如:

.venv/
__pycache__/
*.pyc

uv.lock 与 .python-version 是否提交,需要结合项目类型和团队策略,但对于应用项目,我通常建议:

uv.lock       → 提交
.python-version → 提交
.venv         → 不提交

这样开发人员、CI 和部署环境都可以得到一致的依赖基线。


四十五、uv.lock 要不要手工修改?

不建议。

官方明确将 uv.lock 定义为由 uv 管理的 lockfile。

因此:

不要:

vim uv.lock

而应该:

uv add xxx
uv remove xxx
uv lock

让 uv 修改它。


四十六、生产环境如何保证可复现?

推荐:

开发者
   ↓
pyproject.toml
   ↓
uv lock
   ↓
uv.lock
   ↓
Git
   ↓
CI
   ↓
uv sync --locked
   ↓
Docker
   ↓
Production

这里最重要的是:

uv sync --locked

而不是:

uv sync

因为生产环境应该避免:

构建时重新解析依赖。

否则:

今天构建
→ A 1.2

一个月后构建
→ A 1.3

就可能出现环境漂移。


四十七、AI 项目为什么特别适合 uv?

对于现在的 Python AI 项目:

LangChain
LangGraph
FastAPI
Pydantic
OpenAI SDK
Anthropic SDK
FAISS
Chroma
Transformers
PyTorch

依赖关系往往非常复杂。

例如:

LangGraph
  ↓
LangChain
  ↓
Pydantic
  ↓
typing extensions

同时:

PyTorch
  ↓
CUDA / CPU
  ↓
不同平台 wheel

传统:

pip install
requirements.txt

在复杂环境下很容易演变成:

requirements.txt
requirements-dev.txt
requirements-cuda.txt
requirements-cpu.txt
requirements-linux.txt

而 uv 的:

universal lockfile
+
dependency groups
+
package indexes
+
environment markers
+
workspace

非常适合这类复杂项目。


四十八、对于你这种 Python 后端 / AI 应用开发者,应该怎么用?

如果是:

FastAPI
+
LangGraph
+
LangChain
+
Pydantic
+
PostgreSQL
+
Redis

我建议直接采用:

uv
│
├── Python 版本
│
├── pyproject.toml
│
├── uv.lock
│
├── .venv
│
├── dependency groups
│
├── uv run
│
└── uvx

而不是:

pyenv
+
venv
+
pip
+
pip-tools
+
pipx
+
requirements.txt

四十九、一个比较完整的现代 Python 工作流

初始化:

uv init

指定 Python:

uv python pin 3.12

添加运行时依赖:

uv add fastapi
uv add pydantic
uv add sqlalchemy

添加开发依赖:

uv add 
uv add 

同步:

uv sync

运行:

uv run python -m my_app

测试:

uv run pytest

代码检查:

uv run ruff check .

查看依赖:

uv tree

升级依赖:

uv lock --upgrade

构建:

uv build

发布:

uv publish

整个项目的生命周期就可以统一到:

uv
 │
 ├── init
 ├── python
 ├── add
 ├── remove
 ├── lock
 ├── sync
 ├── run
 ├── tree
 ├── build
 └── publish

五十、几个容易踩坑的地方

1. 不要把 uv pip 和 uv add 混为一谈

如果是 uv project:

uv add xxx

通常优于:

uv pip install xxx

因为前者会维护:

pyproject.toml
+
uv.lock
+
project environment

2. 不要手工修改 uv.lock

让 uv 管理。


3. 不要把 .venv 提交 Git

.venv 是环境,不是项目源代码。


4. 不要忽略 Python 版本

生产环境最好明确:

requires-python = ">=3.12,<3.13"

或者根据实际兼容范围定义。

同时:

.python-version

固定开发环境。


5. Docker 中注意缓存目录和构建策略

如果 Dockerfile 没有正确利用 uv cache,那么 uv 的性能优势会被大幅削弱。


6. 不要滥用 dependency override

如果 resolver 无法解决:

先检查依赖关系
→ 升级/降级直接依赖
→ 寻找兼容版本
→ 最后才考虑 override

五十一、如何理解 uv 的真正价值?

如果只把 uv 看成:

pip 的 Rust 重写

实际上低估了它。

更准确的理解是:

                uv
                 │
      ┌──────────┼──────────┐
      │          │          │
 Python       Project      Tools
 Runtime      Management   Management
      │          │          │
      │          │          │
 python      pyproject     uvx
 install     uv.lock       tool
 pin         sync          install
      │          │          │
      └──────────┼──────────┘
                 │
          Dependency Resolver
                 │
          Global Cache
                 │
          Package Index

它真正解决的是:

Python 项目的完整生命周期管理问题。

而不仅仅是:

如何安装一个 Python 包。


五十二、最终应该如何选择?

如果你是一个刚接触 Python 的开发者:

uv

可以直接作为默认工具。

如果你是多年 Python 开发者:

pip
venv
requirements.txt

也没有必要马上全部抛弃。

更合理的路线是:

现有项目
   │
   ↓
uv pip
   │
   ↓
验证兼容性
   │
   ↓
pyproject.toml
   │
   ↓
uv.lock
   │
   ↓
uv project

而新项目则可以直接:

uv init

开始。


五十三、总结

uv 最值得关注的地方并不是“比 pip 快多少”,而是它把 Python 开发过程中长期分散的工具链重新进行了整合:

                过去

pyenv ────── → Python
venv ─────── → 环境
pip ──────── → 包
pip-tools ── → 锁定
pipx ─────── → CLI
Poetry ───── → 项目
setuptools ─ → 构建
twine ────── → 发布


                ↓


                uv

        ┌───────────────┐
        │      uv       │
        ├───────────────┤
        │ Python        │
        │ Environment   │
        │ Dependencies  │
        │ Resolver      │
        │ Lockfile      │
        │ Tools         │
        │ Scripts       │
        │ Workspace     │
        │ Build         │
        │ Publish       │
        └───────────────┘

对于现代 Python 项目而言,最值得掌握的不是几十个 uv 命令,而是下面这套模型:

pyproject.toml
      │
      │ 声明
      ↓
依赖需求
      │
      │ resolver
      ↓
uv.lock
      │
      │ sync
      ↓
.venv
      │
      │ run
      ↓
Application

再进一步:

.python-version
       │
       ↓
Python Runtime

pyproject.toml
       │
       ↓
Project Definition(定义)

uv.lock
       │
       ↓
Reproducible Resolution(可复现解析)

.venv
       │
       ↓
Project Environment

uv run
       │
       ↓
Execution

这套模型实际上比“pip install 安装几个包”高一个抽象层次。

对于 FastAPI、Django、LangChain、LangGraph、AI Agent、数据处理、CLI 工具以及大型 Python Monorepo 等现代项目,uv 已经不只是一个包安装器,而更接近 Python 项目的基础设施级工具链 。


官方资料