前言
我们从uv官网可以看到uv有下面亮点:
🚀 一个工具替代 pip、pip-tools、pipx、poetry、pyenv、twine、virtualenv 等
⚡️ 比 pip 快 10-100 倍
🗂️ 提供全面的项目管理功能,包含通用锁文件
❇️ 运行脚本,支持内联依赖元数据
🐍 安装和管理 Python 版本
🛠️ 运行和安装 以 Python 包形式发布的工具
🔩 包含 pip 兼容接口,在熟悉 CLI 的同时获得性能提升
🏢 支持 Cargo 风格的工作区用于可扩展项目
💾 磁盘空间高效,通过全局缓存实现依赖去重
⏬ 无需 Rust 或 Python 即可通过 curl 或 pip 安装
🖥️ 支持 macOS、Linux 和 Windows
本文主要对每一条亮点进行剖析
一个工具替代 pip、pip-tools、pipx、poetry、pyenv、twine、virtualenv 等
背景:Python 生态中这些工具都是干什么的?
在传统的 Python 开发流程中,开发者通常需要组合使用多个工具来完成不同任务。下面简单说明每个工具的作用:
| 工具 | 主要用途 |
|---|---|
| pip | 安装 Python 包(从 PyPI 下载并安装) |
| pip-tools | 管理依赖的"锁定"(比如从 requirements.in 生成精确版本的 requirements.txt) |
| pipx | 安装和运行 Python 应用程序(比如全局安装 black、pytest 等 CLI 工具,但隔离环境) |
| poetry | 项目依赖管理 + 虚拟环境 + 打包发布(一体化工具,类似 Node.js 的 npm) |
| - pyenv | 管理多个 Python 版本(比如同时有 Python 3.9、3.11,并切换使用) |
| twine | 将打包好的 Python 包上传到 PyPI(安全上传工具) |
| virtualenv / venv | 创建隔离的 Python 虚拟环境(避免包冲突) |
这些工具各有各的配置方式、命令语法、缓存机制,甚至互相之间还有重叠功能(比如 poetry 也能做 virtualenv 的事)。对新手来说学习成本高,对老手来说维护也麻烦。
uv 是怎么"一个工具替代它们"的?
uv(由 Astral 开发,Rust 编写)的目标是:用一个统一、快速、兼容的命令行工具,覆盖上述所有常见工作流。具体来说:
✅ 替代 pip:
- uv pip install requests ≈ pip install requests
- 但它快很多(因为用 Rust 写,解析依赖更快,支持并行下载等)
✅ 替代 pip-tools: - uv 支持从 requirements.in 生成锁定的 requirements.txt(即"解析依赖树并固定版本")
- 命令如 uv pip compile requirements.in(类似 pip-compile)
✅ 替代 pipx: - uv tool install black → 全局安装 black,但自动创建隔离环境(就像 pipx)
- 还能列出、升级、卸载这些"工具"
✅ 替代 virtualenv: - uv venv 可以快速创建虚拟环境(比 python -m venv 快很多)
- 因为 uv 内置了 Python 解释器发现和环境创建逻辑
✅ 部分替代 pyenv: - uv 本身不编译或安装新的 Python 版本(这点和 pyenv 不同)
- 但它能自动发现系统中已有的多个 Python 版本,并在创建 venv 或安装包时选择指定版本
- 所以如果你已经用 pyenv、asdf、官方安装等方式装好了多个 Python,uv 可以直接用它们 ------ 不需要再调用 pyenv 切换
- 但如果你要"安装新版本 Python",uv 可能还不支持(截至 2026 年初,应该仍需依赖 pyenv 等工具)
📌 所以严格来说,uv 不是完全替代 pyenv,而是绕过 pyenv 的切换环节,直接使用已存在的 Python 版本。这点要特别注意。
✅ 替代 twine(部分):
- uv publish 可以构建并上传 wheel/sdist 到 PyPI(类似 python -m build && twine upload)
- 自动处理认证(读取 .pypirc 或环境变量),更简洁
✅ 替代 poetry(部分):
- uv 目前不提供 poetry 那样的 pyproject.toml 项目声明式管理(比如 [tool.poetry.dependencies])
- 但它可以高效执行 poetry 最核心的两个任务:解析依赖 + 创建虚拟环境
- 所以如果你只用 poetry 来管理依赖和虚拟环境,uv 可能足够;但如果你重度依赖 poetry 的打包、脚本、插件生态,可能还不能完全替换
⚡️ 比 pip 快 10-100 倍
快"具体指哪些操作?
uv 的"快"主要体现在以下几类常见任务中:
| 操作 | 传统工具(如 pip) | uv |
|---|---|---|
| 安装依赖(从 PyPI) | 串行下载 + Python 解析依赖 | 并行下载 + Rust 高效解析 |
| 解析依赖关系(dependency resolution) | 使用较慢的回溯算法 | 使用现代 SAT 求解器(类似 PubGrub) |
| 创建虚拟环境(venv) | 调用 python -m venv,复制标准库 |
直接链接/克隆已有的 Python 安装,避免重复拷贝 |
| 生成锁定文件(如 requirements.txt) | pip-tools 启动多次 Python 进程 |
单次 Rust 进程内完成全部计算 |
所以,"10-100 倍"不是泛指所有操作,而是在典型工作流中最耗时的部分------尤其是依赖解析和安装。
为什么能快这么多?技术原因详解
✅ 1. 用 Rust 编写,而非 Python
- pip 是纯 Python 写的,每次运行都要启动 CPython 解释器、加载模块、解释执行。
- 而 uv 是 Rust 编译成的原生二进制程序,启动快、内存效率高、无解释开销。
- Rust 的并发模型也更安全高效,适合做并行下载和解析。
📌 举例:pip install django 可能要 2~3 秒光是启动和初始化;而 uv pip install django 几乎瞬间开始下载。
✅ 2. 并行下载 wheel 文件
- pip 默认是串行下载(一个包下一个),即使有多个依赖。
- uv 默认并行下载多个 wheel(利用 HTTP/2 或多连接),极大减少 I/O 等待时间。
- 尤其在安装大型项目(如 requirements.txt 有 50+ 包)时,差距非常明显。
✅ 3. 高效的依赖解析器(基于 PubGrub 算法)
- pip 的依赖解析器在过去几年才逐渐改进(pip ≥20.3 引入新解析器),但仍可能在复杂依赖中变慢或失败。
- uv 使用了经过优化的 PubGrub 算法实现(和 Dart pub、npm v7+ 类似),能快速找到满足所有约束的版本组合。
- 更重要的是:整个解析过程在 Rust 中完成,不调用 Python,避免了大量对象创建和 GC 开销。
💡 举个极端例子:
安装像 apache-airflow 这种有上百个可选依赖的包,pip 可能卡住几分钟甚至超时;
而 uv 可能在 10 秒内完成解析 + 下载。
✅ 4. 智能缓存机制
- uv 会缓存:
下载的 wheel(按哈希存储)
解析结果(依赖树)
元数据(PyPI 的 package index)
缓存格式高度优化,读取极快。 - 而 pip 的缓存虽然存在,但粒度较粗,且每次仍需重新解析部分信息。
✅ 5. 虚拟环境创建优化
- uv venv 不是简单调用 python -m venv,而是:
如果目标 Python 版本已存在,直接硬链接或 copy-on-write标准库文件(在支持的文件系统上)
避免重复解压和复制几十 MB 的标准库 - 结果:创建 venv 从 1~2 秒 → 0.1 秒以内
🧪 实测数据(官方及社区):
创建 venv:快 8--50 倍
安装 Django + 依赖:快 10--30 倍
解析复杂依赖(如 pandas + airflow + mlflow):快 50--100 倍
🗂️ 提供全面的项目管理功能,包含通用锁文件
什么是"项目管理功能"?uv 到底管什么?
在 Python 生态中,"项目管理"通常包括:
- 声明项目依赖(比如需要 requests>=2.25)
- 解析并锁定具体版本(比如确定用 requests==2.31.0)
- 创建隔离环境(virtual environment)
- 安装依赖到环境中
- 支持开发依赖(如测试、格式化工具)
- 支持多 Python 版本兼容性声明
传统上,这些功能分散在不同工具中:
- requirements.txt + pip → 简单但无版本解析
- pip-tools → 能生成锁定文件,但流程繁琐
- poetry / pdm → 一体化,但使用自定义格式(如 pyproject.toml 中的 [tool.poetry])
而 uv 的目标是:提供类似 poetry 的项目管理能力,但兼容现有标准,不强制新格式。
📌 注意:截至 2026 年初,uv 的"项目管理"功能仍在演进中。它可能尚未完全对标 poetry 的所有特性(比如脚本定义、插件系统),但核心的依赖声明与锁定已经支持。
重点来了:什么是"通用锁文件"(universal lock file)?
这是 uv 非常关键的一个创新点。
🔒 传统锁文件的问题
| 工具 | 锁文件格式 | 问题 |
|---|---|---|
| pip + pip-tools | requirements.txt |
只针对单一平台/Python 版本;无法表达可选依赖、extras |
| poetry | poetry.lock |
功能强大,但仅 poetry 能读,其他工具无法使用 |
| npm / Cargo | package-lock.json / Cargo.lock |
标准化、跨平台、工具无关 |
Python 长期缺乏一个标准化、跨平台、跨工具的锁文件格式。
✅ uv 的解决方案:uv.lock(或兼容格式)
uv 引入了一种新的锁文件格式(目前是 uv.lock,未来可能成为 PEP 标准),它的特点是:
- 平台无关(universal)
一个锁文件可以包含多个平台(Windows/macOS/Linux)所需的 wheel 信息。
也支持多个 Python 版本(如 3.9、3.10、3.11)的兼容性记录。
这意味着:你在 macOS 上生成的锁文件,可以直接在 Linux CI 上使用,无需重新解析。 - 包含完整元数据
每个包记录:
名称、版本
所有依赖项(含可选依赖 optional dependencies)
对应的 wheel URL 或 hash(用于验证)
适用的 Python 版本范围、操作系统、架构
类似于 npm 的 lock 文件,但为 Python 定制。 - 可被其他工具读取(目标)
uv 团队希望这个格式未来能成为社区标准(类似 requirements.txt 的升级版)。
虽然目前主要由 uv 自己使用,但格式是公开、文档化的,其他工具理论上可以实现支持。
uv 如何使用这个锁文件?工作流示例
假设你有一个项目:
toml
# pyproject.toml(标准 PEP 621 格式)
[project]
name = "myapp"
dependencies = ["httpx", "click"]
requires-python = ">=3.9"
步骤 1:生成锁文件
uv
uv lock
→ 生成 uv.lock,里面精确记录了 httpx0.27.0、click8.1.7,以及它们各自的子依赖、适用平台等。
步骤 2:安装依赖(基于锁文件)
uv
uv sync
→ uv 会严格按照 uv.lock 中的内容安装,确保每次安装结果完全一致(即使 PyPI 上发布了新版本也不会影响)。
这类似于:
- npm ci(基于 package-lock.json 安装)
- cargo install --locked
对比传统 pip:
- pip install -r requirements.txt 无法保证"今天装和明天装是一样的",除非你手动把所有子依赖都写死(很难维护)。
这里需要注意单纯靠requirements.txt虽然大部分场景是可以保证运行一致的,但是也有比如:
(1)不同平台(OS / 架构)可能安装不同的 wheel
(2)pip freeze 不包含"依赖来源"和"哈希校验"
(3)Python 版本变化可能导致隐式不兼容
- 而 uv 的 lock + sync 流程天然保证可重现构建(reproducible builds)。
全面的项目管理功能"还包括什么?
除了锁文件,uv 还提供:
| 功能 | 命令示例 | 说明 |
|---|---|---|
| 添加依赖 | uv add requests |
自动更新 pyproject.toml 和 uv.lock |
| 移除依赖 | uv remove requests |
同上 |
| 安装开发依赖 | uv add --group dev pytest |
支持 PEP 621 的 optional-dependencies 或 dependency-groups(如果标准落地) |
| 切换 Python 版本 | uv venv --python 3.11 |
自动使用系统中已有的 3.11 |
| 同步环境 | uv sync |
按锁文件创建/更新虚拟环境 |
📌 注意:uv 的项目管理命令(如 uv add)依赖 pyproject.toml 使用 PEP 621 标准格式(即 [project] 表)。
如果你的项目还在用 setup.py 或非标准 pyproject.toml,可能需要先迁移。
❇️ 运行脚本,支持内联依赖元数据
先理解背景:Python 脚本的"依赖困境"
假设你想写一个简单的 Python 脚本,比如 download_data.py,它需要 requests 和 tqdm:
python
# download_data.py
import requests
from tqdm import tqdm
# ... 下载逻辑
问题来了:
- 你不能直接运行 python download_data.py,因为用户可能没装 requests。
- 传统做法:
- 写个 requirements.txt
- 让用户先 pip install -r requirements.txt
- 再运行脚本
但这对单文件脚本来说太重了!尤其当你只是想分享一个小工具时。
其他语言是怎么做的?
- Node.js:可以在 package.json 里声明依赖,或者用 npx 直接运行带依赖的脚本。
- Rust:用 cargo script(第三方)支持在脚本顶部声明依赖。
- Shell:虽然没依赖管理,但至少是自包含的。
- Python 一直缺乏这种"自包含可执行脚本 + 自动依赖安装"的能力。
python
# /// script
# requires-python = ">=3.9"
# dependencies = [
# "requests>=2.25",
# "tqdm",
# ]
# ///
import requests
from tqdm import tqdm
print("Downloading...")
# ... your code
然后你只需运行:
python
uv run download_data.py
uv 会:
- 读取脚本顶部的 /// script ... /// 块(这是 uv 定义的特殊注释格式)
- 解析 dependencies 和 requires-python
- 自动创建一个临时虚拟环境(或复用缓存)
- 安装指定依赖
- 在该环境中运行脚本
✅ 用户无需提前安装任何东西(除了 uv 本身)!
技术细节:什么是"内联依赖元数据"?
- "内联" = 依赖信息直接写在脚本文件内部(而不是外部的 requirements.txt 或 pyproject.toml)
- "元数据" = 关于依赖的描述信息,如包名、版本约束、Python 版本要求等
这种设计参考了:
-
PEP 723("Inline script metadata")------这是 Python 官方正在推进的一个提案(截至 2026 年初,可能已接受或处于 Final 阶段)
-
uv 是首个主流工具实现 PEP 723 草案的
📌 注意:
/// script ... /// 这个语法不是任意注释,而是 PEP 723 规定的标准格式。
其他工具未来应该也能支持,从而形成生态标准。
当然uv还 支持的另一种更简洁的内联依赖语法:
python
# uv: requests rich
import requests
from rich import print
print("Hello, world!")
🐍 安装和管理 Python 版本
这句话乍看之下非常像 pyenv 的功能("安装和管理多个 Python 版本"),但 uv 的实现方式、能力边界和设计哲学其实有显著不同。我们来细致分析。
传统方案:pyenv 是怎么工作的?
pyenv 是 Python 社区广泛使用的工具,它的核心能力是:
- 从源码编译安装任意 CPython 版本(如 3.9.18、3.12.0 等)
- 全局或局部切换默认 Python 版本
- 支持其他发行版(如 PyPy、Anaconda)
但它也有缺点:
- 编译 Python 需要安装 build-essential、openssl-dev 等系统依赖
- 编译过程慢(几分钟)
- Windows 支持较弱(需用 pyenv-win)
uv 是如何"安装和管理 Python 版本"的?
关键点来了:uv 并不从源码编译 Python(截至 2026 年初)。
它采用了一种更现代、更快捷的方式:
✅ uv 从预编译的二进制分发版(如 python-build-standalone)直接下载并安装 Python
🔧 技术细节:
uv 内置了对 python-build-standalone 项目的集成。
- 这个项目由 Gregory Szorc(Mercurial 作者)维护
- 提供官方 CPython 的静态链接、无外部依赖的二进制包
- 支持 Linux、macOS、Windows,且开箱即用(无需系统库)
当你运行 uv python install 3.11,uv 会:
- 从 CDN 下载对应平台的预编译 Python 二进制包(通常是 .tar.gz 或 .zip)
- 解压到 uv 的内部目录(如 ~/.local/share/uv/python/)
- 创建符号链接或注册版本
整个过程通常 10~30 秒完成,无需编译!
uv 的 Python 管理命令示例
uv
# 列出所有可用的 Python 版本
uv python list --all
# 安装指定版本(自动下载预编译版)
uv python install 3.11
uv python install 3.12.1
# 查看已安装的版本
uv python list
# 在项目中使用特定版本创建虚拟环境
uv venv --python 3.11
# 直接运行脚本使用指定 Python
uv run --python 3.12 script.py
📌 注意:uv python install 只管理 uv 自己安装的 Python,不会动你系统已有的 /usr/bin/python3 或 pyenv 安装的版本。
通俗总结:安装和管理 Python 版本" = uv 让你像安装普通软件一样,一键下载并使用任意主流 Python 版本,无需编译、无需配置、跨平台一致。
🛠️ 运行和安装 以 Python 包形式发布的工具
这句话的核心是:uv 能像 pipx 一样,方便地安装和运行"命令行工具类"的 Python 包(如 black、ruff、httpie),但体验更集成、更快、更统一。
我们一步步来拆解。
背景:什么是"以 Python 包形式发布的工具"?
很多开发者用 Python 写 命令行工具(CLI),并通过 PyPI 发布。例如:
| 工具 | 功能 | 安装后提供的命令 |
|---|---|---|
black |
代码格式化 | black |
ruff |
快速 Linter | ruff |
httpie |
人性化的 HTTP 客户端 | http |
poetry |
依赖管理 | poetry |
cookiecutter |
项目模板生成 | cookiecutter |
这些包的特点:
- 主要用途是提供 可执行命令,而不是被其他代码 import
- 通常希望 全局可用(在任何目录都能运行)
- 但又不希望污染系统 Python 环境(避免依赖冲突)
传统方案:pipx 是怎么做的?
pipx 就是为解决这个问题而生的:
pip
# 安装 black 到隔离环境,并将 `black` 命令加入 PATH
pipx install black
# 运行(无需激活环境)
black --version
原理:
- 为每个工具创建独立的虚拟环境
- 在该环境中 pip install 工具包
- 将工具的可执行脚本(如 ~/.local/bin/black)软链接到一个公共 bin 目录
- 用户只需把该目录加入 PATH
✅ 优点:隔离、干净、全局可用
❌ 缺点:多一个工具(需单独安装 pipx)、速度一般、与 pip/venv 生态割裂
uv 是如何替代 pipx 的?
uv 内置了完全兼容 pipx 工作流的功能,命令非常相似,但更快、更集成。
✅ 基本用法:
uv
# 安装工具(自动创建隔离环境)
uv tool install black
# 运行(自动找到并执行)
uv tool run black --version
# 或直接使用命令(如果 uv 的 bin 目录在 PATH 中)
black --version
📌 注意:uv tool install 会把可执行文件(如 black)放在 ~/.local/bin/(Linux/macOS)或 %APPDATA%\Python\Scripts(Windows),和 pipx 默认位置一致。
所以如果你已经把 ~/.local/bin 加入 PATH,安装后可以直接运行命令,无需前缀 uv tool run。
uv 的"tool"功能有哪些优势?
- ⚡️ 速度极快
- 因为 uv 本身解析依赖、下载 wheel 极快(Rust + 并行)
- 创建隔离环境也快(uv venv 优化)
- 实测:uv tool install black 比 pipx install black 快 5--10 倍
- 🔗 与 uv 全家桶无缝集成
- 使用相同的缓存(wheel、解析结果)
- 支持相同的镜像源配置
- 可以指定 Python 版本:
python
uv tool install --python 3.11 black
- 🧰 完整的工具管理命令
| 命令 | 作用 |
|---|---|
uv tool install black |
安装 |
uv tool uninstall black |
卸载 |
uv tool list |
列出已安装工具 |
uv tool upgrade black |
升级 |
uv tool run black ... |
临时运行(即使未安装,可自动安装后运行) |
🔩 包含 pip 兼容接口,在熟悉 CLI 的同时获得性能提升
这句话的核心价值是:uv 让你"不用学新命令",就能直接享受 10--100 倍的速度提升。这对开发者体验(DX)至关重要。我们来细致分析。
什么是 "pip 兼容接口"?
简单说:uv 提供了一组命令,其参数、选项、行为与 pip 几乎完全一致,你可以把 pip 直接替换成 uv pip,现有脚本和习惯无需改变。
✅ 示例对比:
| 场景 | 传统 pip 命令 | uv 等效命令 |
|---|---|---|
| 安装包 | pip install requests |
uv pip install requests |
| 从文件安装 | pip install -r requirements.txt |
uv pip install -r requirements.txt |
| 卸载包 | pip uninstall requests |
uv pip uninstall requests |
| 列出已安装包 | pip list |
uv pip list |
| 显示包信息 | pip show requests |
uv pip show requests |
| 下载但不安装 | pip download -r reqs.txt |
uv pip download -r reqs.txt |
📌 注意:命令是 uv pip ...,而不是直接 uv install ...(虽然 uv 也有自己的项目管理命令,但这是另一套)。
为什么"兼容"如此重要?
- 零学习成本迁移
- 团队现有的 CI/CD 脚本、Dockerfile、Makefile 中大量使用 pip install
- 只需全局替换 pip → uv pip,无需重写逻辑
- 例如 Dockerfile:
python
# 原来
RUN pip install -r requirements.txt
# 现在(只需改一行)
RUN uv pip install -r requirements.txt
→ 构建速度可能从 2 分钟降到 10 秒!
- 与现有工具链无缝集成
- 工具如 tox、nox、pre-commit 默认调用 pip
- 虽然它们可能还不直接支持 uv,但你可以通过配置让它们使用 uv pip(例如设置 VIRTUALENV_PIP 或自定义 install command)
- 降低心理门槛
- 很多开发者对"新工具"有抵触:"又要学一套新命令?"
- uv 说:"不用,你继续用 pip install 的方式,只是前面加个 uv。"
🏢 支持 Cargo 风格的工作区用于可扩展项目
这句话提到了 "Cargo 风格的工作区",这是从 Rust 生态借鉴的一个强大项目组织模式。uv 将其引入 Python,旨在解决大型、多包 Python 项目(monorepo 或 multi-package repo)的依赖管理和构建难题。
我们一步步来拆解。
先理解背景:什么是 "Cargo 风格的工作区"?
在 Rust 中,Cargo 是官方构建工具。它的 "workspace" 功能允许你:
-
在一个代码仓库(repo)中管理多个相互依赖的 crate(包)
-
所有子包共享一个顶层 Cargo.toml 声明工作区成员
-
依赖解析是全局统一的(避免版本冲突)
-
构建、测试、发布可以跨包协调
例如:my-rust-project/
├── Cargo.toml ← 工作区根配置
├── crates/
│ ├── core/ ← 子包1
│ │ └── Cargo.toml
│ └── web-api/ ← 子包2(依赖 core)
│ └── Cargo.toml
这种模式非常适合:
- 微服务架构(每个服务是一个包)
- 库 + 示例 + CLI 工具共存
- 插件系统(核心库 + 多个插件包)
Python 的痛点:缺乏原生工作区支持
传统 Python 项目面临以下问题:
| 问题 | 说明 |
|---|---|
| ❌ 每个包独立管理依赖 | 如果 repo 中有 lib/、cli/、web/ 三个包,每个都有自己的 requirements.txt 或 pyproject.toml,容易出现 lib 用 requests==2.28,而 web 用 requests==2.31 → 冲突或冗余 |
| ❌ 跨包开发体验差 | 修改 lib 后,要手动 pip install -e lib/ 才能在 web 中看到变更 |
| ❌ CI 脚本复杂 | 需为每个子包单独设置测试、构建、发布流程 |
虽然工具如 tox、nox、poetry(通过插件)能部分缓解,但没有统一标准。
uv 是如何实现 "Cargo 风格工作区" 的?
uv 引入了 工作区(workspace)概念,通过顶层 pyproject.toml 声明多个成员包,并提供统一命令操作整个工作区。
✅ 基本结构示例:
my-python-project/
├── pyproject.toml ← 工作区根配置
├── packages/
│ ├── core/ ← 子包1
│ │ └── pyproject.toml
│ └── cli/ ← 子包2(依赖 core)
│ └── pyproject.toml
🔧 根 pyproject.toml 内容:
[tool.uv.workspace]
members = ["packages/core", "packages/cli"]
📌 注意:这是 uv 特有的配置([tool.uv.workspace]),目前不是 PEP 标准,但设计上参考了 Rust 和 npm workspaces。
工作区带来的核心能力
- 统一依赖解析(全局锁定)
uv 会一次性解析所有成员包的依赖,生成一个共享的 uv.lock
确保所有子包使用兼容的依赖版本(避免 diamond dependency 问题)
类似于 npm install 在 monorepo 中的行为 - 一键安装整个工作区
uv
uv sync
→ 自动:
- 为每个成员包创建开发环境(或共享环境)
- 以可编辑模式(-e)安装所有本地包
- 安装所有依赖(包括跨包依赖)
结果:修改 core 代码后,cli 立刻能用到最新版本,无需手动重装。
- 跨包脚本运行
uv
# 在 cli 包中运行命令,自动包含 core 的依赖
uv run --package cli python -m cli.main
💾 磁盘空间高效,通过全局缓存实现依赖去重
这句话直击 Python 依赖管理中的一个"隐形成本"问题:重复下载和存储相同的包,浪费大量磁盘空间和带宽。uv 通过精心设计的全局缓存机制来解决这个问题。我们来细致分析。
传统 pip 的缓存问题:为什么磁盘浪费严重?
📦 场景举例:
假设你有 3 个 Python 项目:
- project-a 使用 requests==2.31.0
- project-b 也使用 requests==2.31.0
- project-c 同样使用 requests==2.31.0
当你用传统方式管理:
cd project-a && python -m venv venv && source venv/bin/activate && pip install -r requirements.txt
cd ../project-b && python -m venv venv && ... # 重复
cd ../project-c && ...
结果:
- 每个虚拟环境的 venv/lib/python3.x/site-packages/ 中都有一份 完整的 requests 及其子依赖(如 urllib3, certifi)
- 即使是同一个 wheel 文件,也被复制了 3 次
- 如果每个项目还有 50 个公共依赖(如 numpy, pandas),磁盘占用迅速膨胀到 GB 级别
💾 实测:10 个中型项目,pip 管理下可能占用 5--10 GB 的重复依赖!
虽然 pip 有 HTTP 缓存(~/.cache/pip/http)和 wheel 缓存(~/.cache/pip/wheels),但:
- wheel 缓存只在安装时复用,不解决 site-packages 重复问题
- 虚拟环境之间完全隔离,无法共享已安装的包
uv 是如何实现"磁盘空间高效"的?
uv 采用 三层缓存 + 去重安装 策略:
🔹 第一层:全局 wheel 缓存(Global Wheel Cache)
- 所有下载的 wheel 文件(.whl)存储在统一位置:
Linux/macOS: ~/.cache/uv/wheels/
Windows: %LOCALAPPDATA%\uv\wheels\ - 按 包名 + 版本 + 平台 + Python 版本 + hash 唯一索引
- 同一 wheel 只下载一次,后续项目直接复用
🔹 第二层:解析结果缓存(Resolution Cache) - 依赖解析(如 "django 需要哪些子依赖?")结果被缓存
- 避免每次 uv pip install 都重新计算依赖树
- 加速安装过程,间接减少临时文件生成
🔹 第三层(关键):安装时去重(Content-Addressable Store)
这是 uv 最核心的优化:
✅ 所有已安装的包(以 wheel 形式)被存储在一个全局的"内容寻址存储"中
→ 相同内容的包,物理上只存一份
然后,在创建虚拟环境时:
- 不复制整个包,而是通过 硬链接(hard link) 或 copy-on-write(CoW) 引用全局缓存中的文件
- 在支持的文件系统(如 ext4, APFS, NTFS)上,硬链接几乎不占额外空间
🌰 举例:
- requests==2.31.0 的 wheel 解压后占 5 MB
- 10 个项目都用它 → 磁盘只增加 ~5 MB,而不是 50 MB
- 每个 venv 中的 requests 目录只是指向同一组 inode 的硬链接
⚙️ 技术细节:
uv 使用类似 Nix 或 Docker layer cache 的思想,但为 Python 优化。
实际效果:能省多少空间?
| 场景 | 传统 pip | uv | 节省 |
|---|---|---|---|
| 1 个项目(50 个包) | ~300 MB | ~300 MB | --- |
| 10 个相似项目 | ~3 GB | ~600 MB | 80%+ |
| CI 中频繁重建 venv | 每次全量下载+安装 | 复用缓存,增量极小 | 时间 + 空间双节省 |
📊 官方数据(2025 年):
在包含 20 个 Django 项目的 monorepo 中,uv 将依赖存储从 4.2 GB 降至 720 MB。
与 pip 缓存的本质区别
| 特性 | pip 缓存 | uv 缓存 |
|---|---|---|
| 缓存层级 | HTTP + wheel | HTTP + wheel + installed packages |
| 安装方式 | 复制 wheel 到 site-packages | 硬链接 全局已安装包 |
| 跨项目去重 | ❌(每个 venv 独立) | ✔️(全局唯一存储) |
| 清理机制 | pip cache purge |
uv cache clean(更精细) |
| 空间效率 | 低 | 极高 |
用户如何受益?
- 本地开发更轻量
- 可以随意创建/删除虚拟环境,不用担心磁盘爆满
- 笔记本用户尤其受益(SSD 空间宝贵)
- CI/CD 成本降低
- Docker 镜像层可以复用 uv 缓存
- GitHub Actions 等可缓存 ~/.cache/uv 目录,大幅缩短 job 时间
- 团队环境一致性
- 所有人使用相同的全局缓存 → 减少"在我机器上能跑"的问题
⏬ 无需 Rust 或 Python 即可通过 curl 或 pip 安装
先理解字面意思
uv 本身是用 Rust 编写的原生二进制程序(不是 Python 包),但它提供了两种极其简单的安装方式:
✅ 方式 1:通过 curl(或 wget)一键安装(不需要 Rust,也不需要 Python)
python
curl -LsSf https://astral.sh/uv/install.sh | sh
✅ 方式 2:通过 pip 安装(此时需要 Python,但不需要 Rust)
python
pip install uv
🎯 关键点:
如果你完全没有 Python 环境(比如刚装好 Linux 的干净系统),可以用 curl 安装 uv
如果你已有 Python,也可以像装普通包一样用 pip install uv
为什么"无需 Rust"很重要?
- Rust 是编译型语言,要从源码构建 uv,你需要:
(1)安装 Rust 工具链(rustup)
(2)安装 C 编译器、OpenSSL 开发库等系统依赖
(3)执行 cargo build --release - 这对终端用户来说太复杂了!
而 uv 的官方安装脚本(install.sh)会:
- 自动检测你的操作系统(Linux/macOS/Windows WSL)
- 自动检测 CPU 架构(x86_64, aarch64, etc.)
- 从 GitHub Releases 下载预编译好的二进制文件
- 解压到 ~/.local/bin/(或其他合适位置)
- 提示你把该目录加入 PATH
✅ 整个过程不需要 Rust,不需要 Python,甚至不需要 pip。
那"通过 pip 安装"是怎么做到的?(uv 不是 Rust 写的吗?)
这是 uv 团队的一个巧妙设计:
✅ uv 同时以两种形式发布:
- 独立二进制(通过 GitHub Releases 分发,供 curl 安装)
- Python wheel 包(上传到 PyPI,供 pip install uv)
🔧 技术实现:
- 在 PyPI 上的 uv 包其实是一个 "包装器(wrapper)"
- 它包含一个极小的 Python 脚本,作用是:
(1)检测系统架构
(2)从 Astral CDN 下载对应的 uv 二进制
(3)将其放在包内部的 bin/ 目录
(4)提供 uv 命令行入口点(通过 console_scripts)
所以当你运行:
pip install uv
uv --version
实际执行的是 Rust 编译的二进制,而不是 Python 代码!
📌 这种模式称为 "Python-wrapped native binary",类似 docker-compose(新版)、rye 等工具的做法。
🖥️ 支持 macOS、Linux 和 Windows。
这句话看似平淡无奇("现在哪个工具不支持多平台?"),但在 Python 工具链的语境下,跨平台一致性其实是一个长期痛点。uv 不仅"支持"三大平台,更重要的是:在所有平台上提供几乎一致的性能、行为和用户体验。我们来细致分析。
为什么"支持三大平台"在 Python 生态中并不 trivial?
许多 Python 工具虽然标榜"跨平台",但在实际使用中常有差异:
| 问题 | 举例 |
|---|---|
| ❌ Windows 路径/权限问题 | venv 在 Windows 上路径分隔符、可执行文件扩展名(.exe vs 无后缀)处理不一致 |
| ❌ 依赖编译失败 | 包含 C 扩展的包(如 cryptography)在 Windows 上常因缺少 Visual Studio Build Tools 而安装失败 |
| ❌ 脚本入口点差异 | Linux/macOS 用 #!/usr/bin/env python,Windows 需要 .exe 或 .bat wrapper |
| ❌ 性能差距巨大 | 某些工具在 Windows 上因文件系统或进程模型原因慢 2--5 倍 |
而 uv 的目标是:消除这些差异。
uv 如何实现真正的跨平台一致性?
✅ 1. 统一用 Rust 编写核心逻辑
- Rust 本身具有优秀的跨平台能力
- 文件路径处理使用
std::path或camino等库,自动适配 / vs \ - 进程管理、信号处理等都做了平台抽象
✅ 2. 预编译二进制覆盖所有主流平台/架构
uv 官方发布时,会为以下平台生成独立二进制(通过 CI 自动构建):
| 平台 | 架构 | 二进制格式 |
|---|---|---|
| Linux | x86_64, aarch64 (ARM64) | ELF 可执行文件 |
| macOS | x86_64, aarch64 (Apple Silicon) | Mach-O universal binary(可能包含双架构) |
| Windows | x86_64, aarch64 | .exe(PE 格式) |
📌 用户无需关心细节:安装脚本或 PyPI wheel 会自动选择匹配的版本。
✅ 3. 虚拟环境创建行为一致
uv venv 在三大平台上:
- 创建结构相同的目录(bin/ vs Scripts/ 会自动适配)
- 生成正确的激活脚本(activate, activate.bat, Activate.ps1)
- 可执行文件命名规范统一(如 black → Windows 上也会有 black.exe)
✅ 4. 依赖解析与安装逻辑完全相同
无论在哪一平台运行 uv pip install requests: - 使用相同的 PubGrub 解析器
- 应用相同的依赖约束
- 选择符合当前平台标记(markers)的 wheel
结果:锁文件(uv.lock)可在平台间共享(只要锁文件包含多平台元数据)
Windows 支持的特别优化
Windows 曾是 Python 工具的"噩梦平台",uv 做了针对性改进:
🔧 1. 无需手动安装构建工具
-
传统 pip install 在 Windows 上遇到源码包(sdist)时,常报错:
error: Microsoft Visual C++ 14.0 or greater is required.
-
uv 优先使用预编译 wheel(从 PyPI 或缓存)
-
如果必须构建,可能集成 rust 或调用兼容的构建后端(但尽量避免)
🔧 2. 长路径支持
- Windows 默认限制 260 字符路径,而 Python 项目嵌套深时容易超限
- uv 应该启用了 Windows 长路径支持(通过 manifest 或运行时设置)
🔧 3. PowerShell / CMD / WSL 兼容
- uv 命令在 PowerShell、CMD、Git Bash、WSL 中均可正常运行
- 环境变量、路径分隔符自动转换
通俗总结
"支持 macOS、Linux 和 Windows" = uv 不只是"能在三大平台跑",而是"在三大平台上跑得一样快、一样稳、一样简单"。
它解决了 Python 开发者长期以来的跨平台"薛定谔兼容"问题------
不再是"在我机器上能跑",而是"在所有人的机器上都能跑"。
这背后是 Rust 的跨平台能力 + 精心设计的分发策略 + 对各平台细节的深度适配。