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

一、为什么 Python 需要 uv?
Python 的生态非常成熟,但长期以来,项目工程化实际上是由多个工具拼接完成的:
objectivec
Python
├── pyenv / conda → Python 版本管理
├── venv / virtualenv → 虚拟环境
├── pip → 包安装
├── pip-tools → 依赖锁定
├── pipx → CLI 工具隔离
├── Poetry / PDM → 项目依赖管理
├── setuptools → 包构建
└── twine → 包发布
这种工具链并不是不能使用,而是存在明显的问题:
- 工具之间职责分散;
- 不同工具之间存在配置重复;
- Python 版本、虚拟环境和依赖之间缺乏统一管理;
requirements.txt对复杂项目的表达能力有限;- pip 本身并不是完整的项目管理工具;
- 依赖解析和安装速度长期是 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 工具的开发团队。






如果从功能层次来看,可以把 uv 理解成下面这个结构:
csharp
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 实际上存在两套接口:
erlang
uv pip ...
以及:
erlang
uv ...
前者是 pip-compatible interface(兼容 pip 工作流的接口) 。
后者是 uv 原生的 project interface(项目管理接口) 。
例如:
uv pip install requests
和:
csharp
uv add requests
虽然最终都可能安装 requests,但它们表达的语义完全不同。
3.1 uv pip
它更接近传统 Python:
sql
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 项目
推荐的新项目则应该使用:
csharp
uv init
uv add fastapi
uv add pydantic
uv run python main.py
这里的重点变化是:
依赖不再只是"安装到当前环境",而是成为项目声明的一部分。
最终依赖会进入:
pyproject.toml
同时生成:
csharp
uv.lock
因此:
pip install
更像是:
修改一个环境。
而:
csharp
uv add
更像是:
修改一个项目。
这是理解 uv 的第一个关键。
四、uv 的核心项目模型
一个典型的 uv 项目:
csharp
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 项目
直接:
csharp
uv init my-project
进入:
bash
cd my-project
然后:
arduino
uv run python
uv 会自动处理项目环境。
也可以明确创建:
bash
uv sync
此时通常会得到:
csharp
my-project/
├── .venv/
├── pyproject.toml
└── uv.lock
这和传统流程:
python -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txt
相比,少了大量手工步骤。
六、pyproject.toml:uv 项目的核心配置
一个典型配置:
ini
[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 会:
- 读取项目声明;
- 解析依赖;
- 生成或更新
uv.lock; - 创建
.venv; - 安装解析后的依赖。
七、uv add 为什么比 pip install 更重要?
传统项目:
pip install fastapi
安装成功后,你还需要:
pip freeze > requirements.txt
然后手工维护依赖。
uv:
csharp
uv add fastapi
会直接修改:
ini
[project]
dependencies = [
"fastapi>=..."
]
同时更新:
csharp
uv.lock
并同步项目环境。
所以推荐的工作方式是:
csharp
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
解决的是:
我需要什么?
那么:
csharp
uv.lock
解决的是:
到底安装什么?
例如:
ini
dependencies = [
"fastapi>=0.115",
]
并不意味着每次安装都一定得到同一个 FastAPI 版本。
因为 FastAPI 还会依赖:
erlang
starlette
pydantic
typing-extensions
...
这些依赖还有自己的依赖。
最终形成:
fastapi
├── starlette
│ └── anyio
├── pydantic
│ ├── annotated-types
│ └── pydantic-core
└── typing-extensions
uv resolver(依赖解析器)会把整个依赖图进行解析。
最终把结果保存到:
csharp
uv.lock
九、uv.lock 与 requirements.txt 有什么区别?
传统:
requirements.txt
可能是:
ini
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 非常重要、但很多入门文章不会讲清楚的地方。
假设你的项目支持:
ini
requires-python = ">=3.10"
同时需要:
Windows
Linux
macOS
某个依赖可能存在:
css
Linux → A
Windows → B
macOS → C
传统 requirements 文件通常需要针对不同环境生成不同结果。
而 uv 的 universal resolution(通用解析)会在一个 lockfile 中表达这些条件。
例如概念上可能形成:
ini
package-x==1.0 ; sys_platform == "linux"
package-x==2.0 ; sys_platform == "win32"
因此:
csharp
一个项目
│
└── 一个 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(积极缓存)。
例如:
css
Global Cache
│
┌──────┼──────┐
│ │ │
Project A Project B Project C
│ │ │
└──────┼──────┘
│
Shared Packages
同一个依赖不需要在每个项目里重复下载。
官方文档特别强调,uv 的缓存目录最好与 Python 环境位于同一文件系统,否则无法充分利用链接机制,可能退化为复制操作。(Astral 文档)
十二、uv 的缓存机制
可以查看缓存目录:
bash
uv cache dir
清理缓存:
uv cache clean
清理无用缓存:
uv cache prune
如果遇到:
css
Failed to hardlink files
Falling back to full copy
这类提示,本质上通常意味着:
go
cache
↓
无法进行 hardlink
↓
fallback 到 copy
↓
性能下降
特别是在:
- Windows;
- Docker;
- 跨文件系统;
- 网络文件系统;
中更容易遇到。
因此,uv 的缓存并不是简单的"下载缓存",而是其性能架构的一部分。
十三、uv run:推荐的项目运行方式
传统 Python:
bash
source .venv/bin/activate
python main.py
uv:
arduino
uv run python main.py
FastAPI:
arduino
uv run uvicorn app.main:app
Pytest:
arduino
uv run pytest
Ruff:
arduino
uv run ruff check .
它的一个重要特点是:
uv run会在运行命令前检查 lockfile 和项目环境是否需要更新。
因此:
arduino
uv run pytest
并不只是:
python pytest
而更接近:
csharp
检查 pyproject.toml
↓
检查 uv.lock
↓
检查 .venv
↓
同步环境
↓
执行 pytest
官方文档明确说明,uv run 会确保命令运行在项目所需的锁定依赖环境中。(Astral 文档)
十四、uv sync 到底做什么?
bash
uv sync
可以理解成:
根据项目声明和 lockfile,把项目环境同步到期望状态。
例如:
bash
pyproject.toml
↓
uv.lock
↓
uv sync
↓
.venv
因此:
csharp
uv add
解决:
修改项目依赖。
而:
bash
uv sync
解决:
让环境符合项目依赖。
十五、uv lock、uv sync、uv run 的区别
这是实际开发中非常容易混淆的三个命令。
| 命令 | 核心职责 |
|---|---|
uv lock |
更新依赖解析结果 |
uv sync |
将环境同步到 lockfile |
uv run |
同步后执行命令 |
可以理解成:
csharp
pyproject.toml
│
│ uv lock
↓
uv.lock
│
│ uv sync
↓
.venv
│
│ uv run
↓
application
十六、--locked 和 --frozen
CI/CD 中非常重要。
--locked
arduino
uv run --locked pytest
含义是:
不允许更新 lockfile。
如果:
csharp
pyproject.toml
≠
uv.lock
就直接失败。
适合 CI。
--frozen
arduino
uv run --frozen pytest
更严格:
直接把已有
uv.lock当作唯一事实来源,不检查它是否需要更新。
如果没有 uv.lock,直接失败。
因此 CI 中经常可以看到类似:
css
uv sync --locked
uv run --locked pytest
或者在完全受控的构建流程中使用 --frozen。
官方 CLI 对两者的语义有明确区分。(Astral 文档)
十七、开发依赖:dependency groups
传统项目通常:
requirements.txt
requirements-dev.txt
uv 推荐:
ini
[dependency-groups]
dev = [
"pytest",
"ruff",
"mypy",
]
然后:
sql
uv add --dev pytest
等价于:
csharp
uv add --group dev pytest
也可以创建更多组:
ini
[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 更好?
假设:
requirements.txt
requirements-dev.txt
requirements-test.txt
requirements-docs.txt
每个文件都可能存在重复依赖:
pytest
ruff
pydantic
而 uv:
ini
[project]
dependencies = [
"fastapi",
]
[dependency-groups]
dev = [
"ruff",
]
test = [
"pytest",
]
docs = [
"mkdocs",
]
这些依赖最终可以进入同一个:
csharp
uv.lock
因此:
bash
一个项目
│
├── production dependencies
├── dev dependencies
├── test dependencies
└── docs dependencies
│
↓
uv.lock
这是大型项目管理上的重要优势。
十九、可选依赖 extras
如果你的项目是一个需要发布的 Python package,可以使用:
ini
[project.optional-dependencies]
postgres = [
"asyncpg",
]
redis = [
"redis",
]
all = [
"asyncpg",
"redis",
]
然后:
bash
uv sync --extra postgres
或者:
bash
uv sync --all-extras
这里需要区分:
bash
dependency groups
和:
arduino
optional dependencies / extras
一般可以理解为:
bash
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
这是两个经常被混淆的概念。
例如:
ini
[project]
requires-python = ">=3.11,<3.14"
表达的是:
这个项目支持哪些 Python 版本。
而:
.python-version
表达的是:
当前开发环境默认使用哪个 Python 版本。
因此:
markdown
requires-python
↓
兼容范围
.python-version
↓
当前项目默认解释器
例如:
ini
requires-python = ">=3.11,<3.14"
同时:
.python-version
3.12
完全合理。
二十二、uvx:Python CLI 工具的新方式
以前安装 Ruff:
pip install ruff
然后:
sql
ruff check .
如果只是临时使用某个工具,就会产生一个问题:
为什么为了运行一个 CLI 工具,要把它安装到当前环境?
uv 提供:
uvx ruff
它实际上等价于:
arduino
uv tool run ruff
uv 会为工具创建隔离环境。
因此:
uvx ruff
uvx black
uvx mypy
不会污染你的项目环境。
官方建议,对于很多"一次性执行"的 Python CLI 工具,uvx 往往比永久安装更加合适。(GitHub)
二十三、uv tool install
如果你需要长期使用某个 CLI:
uv tool install ruff
它会安装到独立的工具环境,但将命令暴露到 PATH。
因此:
markdown
项目依赖
↓
uv add
临时 CLI
↓
uvx
长期 CLI
↓
uv tool install
这是非常清晰的职责划分。
二十四、uv 支持 PEP 723:单文件 Python 应用
uv 还有一个非常有意思的能力:
给一个
.py文件声明自己的依赖。
例如:
python
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "requests",
# "rich",
# ]
# ///
import requests
from rich import print
print(requests.get("https://example.com").status_code)
然后:
arduino
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
甚至:
python
uv pip compile requirements.in
以及:
bash
uv pip sync requirements.txt
因此 uv 并不要求你"一次性重构整个项目"。
可以采用:
csharp
旧项目
│
├── pip
↓
uv pip
│
↓
pyproject.toml
│
↓
uv.lock
│
↓
完整 uv project
官方也专门提供了从 pip 项目迁移到 uv project 的迁移路径。(Astral 文档)
二十六、uv pip compile
如果你的团队仍然依赖:
requirements.in
可以:
css
uv pip compile requirements.in \
--output-file requirements.txt
它类似于:
python
pip-tools / pip-compile
但底层使用 uv 的 resolver(依赖解析器)。
还可以:
bash
uv pip sync requirements.txt
将环境严格同步到 requirements 文件。
因此对于旧项目:
bash
requirements.in
↓
uv pip compile
↓
requirements.txt
↓
uv pip sync
↓
.venv
是一个非常实用的迁移方案。(Astral 文档)
二十七、uv 的依赖解析能力
对于资深 Python 开发者来说,这可能比 CLI 更值得关注。
uv 的 resolver 不只是:
找到一个能安装的版本
而是对整个依赖图进行统一解析。
例如:
css
A
├── C >= 1,<3
│
B
└── C >= 2,<4
那么 resolver 需要找到:
C >=2,<3
如果:
css
A → C <2
B → C >=2
就无法解决。
uv 会报告:
yaml
No solution found
而不是简单地把某个版本装进去。
二十八、Dependency Overrides
真实项目中经常遇到:
css
A → C <2
B → C >=2
但你经过测试后发现:
A 实际上也可以使用 C 2.x。
这时候可以使用 dependency override(依赖覆盖)。
例如:
ini
[tool.uv]
override-dependencies = [
"C>=2",
]
但这应该谨慎使用。
因为:
override 本质上是在告诉 resolver:忽略原始包声明中的某些约束。
如果上游包真的不兼容新版本,那么:
resolver 成功
≠
程序运行正确
因此 override 应该被视为最后手段,而不是解决依赖冲突的常规方式。(Astral 文档)
二十九、Constraint 与 Override 的区别
这是高级依赖管理中非常重要的一组概念。
Constraint(约束)
sql
constraint
是在缩小候选范围。
例如:
shell
C>=2,<3
它不会主动把 C 加入项目,只是在 C 已经出现时限制版本。
Override
arduino
override
则是在告诉 resolver:
忽略原始依赖声明,按照我的规则解析。
所以:
arduino
constraint
→ 缩小范围
override
→ 改写约束
两者不要混用。
三十、Package Index:官方源、私有源与多源
企业项目通常不会只使用 PyPI。
例如:
diff
PyPI
+
公司内部 PyPI
+
PyTorch index
uv 支持配置多个 package index(包索引)。
例如:
ini
[[tool.uv.index]]
name = "pytorch"
url = "https://download.pytorch.org/whl/cpu"
然后:
ini
[tool.uv.sources]
torch = { index = "pytorch" }
这样就可以让不同依赖从不同来源解析。
官方文档支持配置多个 index,并定义它们的优先顺序。(GitHub)
这对于:
- PyTorch;
- CUDA;
- 企业私有包;
- 内部 SDK;
- 镜像仓库;
尤其重要。
三十一、Git 依赖
uv 支持直接:
csharp
uv add git+https://github.com/psf/requests
也可以在:
ini
[tool.uv.sources]
my-package = { git = "https://github.com/example/my-package" }
中定义。
这对于内部组件、尚未发布到 PyPI 的 package 很有价值。
同时 uv 会对 Git dependency(Git 依赖)的解析结果进行缓存,并能够在编译依赖时固定到具体 commit。(Astral 文档)
三十二、Path Dependency
大型 Python 项目经常存在:
markdown
project/
├── app/
└── libs/
└── common/
可以使用:
ini
[tool.uv.sources]
common = { path = "./libs/common" }
开发过程中还可以使用 editable:
ini
[tool.uv.sources]
common = { path = "./libs/common", editable = true }
这样非常适合:
主应用
↓
内部 Python SDK
↓
公共工具库
这种多包开发模式。
三十三、Workspace:大型 Python 仓库的解决方案
如果项目越来越大:
markdown
repo/
├── services/
│ ├── api/
│ ├── worker/
│ └── scheduler/
│
└── libs/
├── common/
└── storage/
你可能不希望每个 package 都完全独立。
uv 提供:
workspace
这个概念来自 Rust Cargo。
一个 workspace 可以:
go
package A
package B
package C
│
↓
一个 uv.lock
每个 package 有自己的:
pyproject.toml
但 workspace 共享:
csharp
uv.lock
因此可以统一管理大型 monorepo(单仓多项目)。(Astral 文档)
三十四、什么时候应该使用 Workspace?
适合:
go
Monorepo
大型 Python 平台
多个内部 SDK
多个相互依赖的 Python package
例如:
javascript
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)
因此:
csharp
开发
↓
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 更强调:
diff
Python
+ package
+ project
+ environment
+ tools
+ scripts
+ workspace
+ build
并且提供:
uv pip
作为传统工作流的兼容入口。
因此 uv 的迁移成本通常比较低。
三十七、uv 与 conda 的区别
conda 不只是 Python package manager。
它实际上管理:
Python
C/C++
系统级依赖
二进制库
科学计算环境
因此:
conda
在科学计算领域仍然有自己的价值。
uv 的核心目标则更加聚焦:
diff
Python runtime
+
Python packages
+
Python projects
对于:
objectivec
FastAPI
Django
LangChain
AI Agent
Web backend
CLI
普通 Python 服务
uv 通常更加轻量。
三十八、uv 与 pyenv 的区别
传统:
go
pyenv
↓
管理 Python 版本
venv
↓
创建虚拟环境
pip
↓
安装 package
uv 可以把这些能力整合:
markdown
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
所以:
objectivec
pipx
↓
隔离 CLI 工具
uv tool
↓
隔离 CLI 工具
uvx
↓
临时执行 CLI 工具
四十、uv 与 Docker
uv 非常适合现代 Python Docker 构建。
例如:
bash
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"]
核心思想是:
bash
先复制依赖描述
↓
uv sync
↓
Docker cache
↓
再复制源代码
这样修改 Python 源代码时,不必每次重新安装所有依赖。
对于 CI/CD,还可以进一步使用:
bash
uv sync --locked
确保构建过程不会偷偷修改锁文件。
四十一、企业项目中推荐的目录结构
对于一个中大型 Python 服务,我更推荐:
css
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:
ini
[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"
然后:
bash
uv sync
开发:
arduino
uv run pytest
uv run ruff check .
uv run mypy src
运行:
arduino
uv run python -m my_service
四十二、uv 常用命令速查
Python 管理
arduino
uv python list
uv python install 3.12
uv python pin 3.12
uv python find
项目
csharp
uv init
uv add fastapi
uv remove fastapi
uv sync
uv lock
uv run
uv tree
开发依赖
sql
uv add --dev pytest
uv add --group lint ruff
uv sync --all-groups
uv sync --no-default-groups
虚拟环境
css
uv venv
uv venv --python 3.12
pip 兼容接口
bash
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
缓存
bash
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
创建:
csharp
uv init
然后迁移:
csharp
uv add -r requirements.txt
将依赖转移到:
ini
[project]
dependencies = [...]
第三阶段:使用 uv.lock
执行:
csharp
uv lock
提交:
csharp
uv.lock
到 Git。
第四阶段:统一开发命令
把:
erlang
python -m pytest
python -m uvicorn ...
ruff ...
逐渐统一为:
arduino
uv run pytest
uv run uvicorn ...
uv run ruff ...
第五阶段:CI/CD
将:
erlang
pip install ...
替换成:
bash
uv sync --locked
这样 CI 环境和开发环境都基于:
csharp
pyproject.toml
+
uv.lock
四十四、生产项目应该提交哪些文件?
通常:
csharp
提交 Git:
pyproject.toml
uv.lock
.python-version
通常不提交:
.venv/
例如:
markdown
.venv/
__pycache__/
*.pyc
uv.lock 与 .python-version 是否提交,需要结合项目类型和团队策略,但对于应用项目,我通常建议:
csharp
uv.lock → 提交
.python-version → 提交
.venv → 不提交
这样开发人员、CI 和部署环境都可以得到一致的依赖基线。
四十五、uv.lock 要不要手工修改?
不建议。
官方明确将 uv.lock 定义为由 uv 管理的 lockfile。
因此:
csharp
不要:
vim uv.lock
而应该:
csharp
uv add xxx
uv remove xxx
uv lock
让 uv 修改它。
四十六、生产环境如何保证可复现?
推荐:
csharp
开发者
↓
pyproject.toml
↓
uv lock
↓
uv.lock
↓
Git
↓
CI
↓
uv sync --locked
↓
Docker
↓
Production
这里最重要的是:
bash
uv sync --locked
而不是:
bash
uv sync
因为生产环境应该避免:
构建时重新解析依赖。
否则:
css
今天构建
→ 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 的:
diff
universal lockfile
+
dependency groups
+
package indexes
+
environment markers
+
workspace
非常适合这类复杂项目。
四十八、对于你这种 Python 后端 / AI 应用开发者,应该怎么用?
如果是:
diff
FastAPI
+
LangGraph
+
LangChain
+
Pydantic
+
PostgreSQL
+
Redis
我建议直接采用:
arduino
uv
│
├── Python 版本
│
├── pyproject.toml
│
├── uv.lock
│
├── .venv
│
├── dependency groups
│
├── uv run
│
└── uvx
而不是:
diff
pyenv
+
venv
+
pip
+
pip-tools
+
pipx
+
requirements.txt
四十九、一个比较完整的现代 Python 工作流
初始化:
csharp
uv init
指定 Python:
uv python pin 3.12
添加运行时依赖:
csharp
uv add fastapi
uv add pydantic
uv add sqlalchemy
添加开发依赖:
sql
uv add --dev pytest
uv add --dev ruff
同步:
bash
uv sync
运行:
arduino
uv run python -m my_app
测试:
arduino
uv run pytest
代码检查:
arduino
uv run ruff check .
查看依赖:
uv tree
升级依赖:
csharp
uv lock --upgrade
构建:
uv build
发布:
uv publish
整个项目的生命周期就可以统一到:
csharp
uv
│
├── init
├── python
├── add
├── remove
├── lock
├── sync
├── run
├── tree
├── build
└── publish
五十、几个容易踩坑的地方
1. 不要把 uv pip 和 uv add 混为一谈
如果是 uv project:
csharp
uv add xxx
通常优于:
uv pip install xxx
因为前者会维护:
diff
pyproject.toml
+
uv.lock
+
project environment
2. 不要手工修改 uv.lock
让 uv 管理。
3. 不要把 .venv 提交 Git
.venv 是环境,不是项目源代码。
4. 不要忽略 Python 版本
生产环境最好明确:
ini
requires-python = ">=3.12,<3.13"
或者根据实际兼容范围定义。
同时:
.python-version
固定开发环境。
5. Docker 中注意缓存目录和构建策略
如果 Dockerfile 没有正确利用 uv cache,那么 uv 的性能优势会被大幅削弱。
6. 不要滥用 dependency override
如果 resolver 无法解决:
arduino
先检查依赖关系
→ 升级/降级直接依赖
→ 寻找兼容版本
→ 最后才考虑 override
五十一、如何理解 uv 的真正价值?
如果只把 uv 看成:
pip 的 Rust 重写
实际上低估了它。
更准确的理解是:
bash
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
也没有必要马上全部抛弃。
更合理的路线是:
csharp
现有项目
│
↓
uv pip
│
↓
验证兼容性
│
↓
pyproject.toml
│
↓
uv.lock
│
↓
uv project
而新项目则可以直接:
csharp
uv init
开始。
五十三、总结
uv 最值得关注的地方并不是"比 pip 快多少",而是它把 Python 开发过程中长期分散的工具链重新进行了整合:
markdown
过去
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 命令,而是下面这套模型:
arduino
pyproject.toml
│
│ 声明
↓
依赖需求
│
│ resolver
↓
uv.lock
│
│ sync
↓
.venv
│
│ run
↓
Application
再进一步:
markdown
.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 项目的基础设施级工具链。