首页
/ uv pip 接口实战指南:作为 pip、pip-tools 与 virtualenv 替代命令的完整工作流

uv pip 接口实战指南:作为 pip、pip-tools 与 virtualenv 替代命令的完整工作流

2026-09-06 11:52:15作者:平淮齐Percy

本文系统讲解 uv 的 uv pip 接口——一套可直接替代 pippip-toolsvirtualenv 常见命令的低级包管理接口。读完你可以掌握:如何创建与定位 Python 虚拟环境、如何安装/卸载/检查包、如何用 pyproject.tomlrequirements.in 声明依赖,以及如何通过 uv pip compile 锁定版本、用 uv pip sync 精确同步环境,并理解 uv 与 pip 行为上的关键差异及其源码实现位置。

定位:一个面向高级用户的低级包管理接口

uv 的 uv pip 子命令组被设计为 pippip-toolsvirtualenv 常用命令的"即插即用"替代(drop-in replacement)。它与 uv 的主力接口(如 uv adduv runuv lock 等项目级命令)的核心区别在于:项目级接口会自动为你管理虚拟环境,而 uv pip 命令则直接操作你手头的虚拟环境。这种定位把 uv 的速度与能力暴露给两类用户:

  • 已经熟悉 pip / pip-tools 工作流、希望无缝切换的高级用户;
  • 尚未准备好完全迁移到 uv 项目接口的存量项目。

有两点前提需要先明确(见 The pip interface):

  1. uv 并不依赖 pip,更不会调用 pip。 之所以保留 pip 这个名字,是为了强调这是一组与 pip 接口对齐的低级命令,并将其与更高层抽象的项目命令区分开。你可以用 uv 替代 pip install,但 uv 内部实现完全是独立的 Rust 代码(核心实现在 crates/uv/src/commands/pip 目录中)。
  2. 这些命令并不是 pip 工具的逐字复刻。 你越偏离常见工作流,越可能遇到行为差异。官方在 兼容指南 中列出了所有已知差异,本文末尾也会汇总要点。

从源码结构看,uv pip 的子命令在 uv-cli 的 PipCommand 枚举 中定义,共九个公开子命令:compilesyncinstalluninstallfreezelist(别名 ls)、showtreecheck,以及一个隐藏的实验性 debug。每个子命令都有对应的独立实现文件(如 install.rssync.rscompile.rs),与本文下面各节一一对应。

创建与使用 Python 虚拟环境

每个 Python 安装都带有一个"活动环境",安装包到该环境后模块才可被脚本导入。最佳实践是不直接修改 Python 安装自带的环境——尤其是操作系统自带的 Python,因为发行版往往自己管理这些包。虚拟环境(virtual environment)正是把包与解释器环境隔离开来的轻量方案。与 pip 不同,uv 默认强制使用虚拟环境

创建虚拟环境

等价于 virtualenvuv venv 命令:

$ uv venv                          # 在 ./.venv 创建虚拟环境
$ uv venv my-name                  # 指定名称或路径
$ uv venv --python 3.11            # 请求特定 Python 版本

--python 3.11 这样的版本请求要求系统上存在对应版本;若不存在,uv 会直接为你下载所需的 Python 解释器(详见 Python 版本管理)。

创建完成后,如果使用默认的 .venv 名称,uv 在后续调用中会自动发现并使用它:

$ uv venv
$ # 在新虚拟环境中安装包
$ uv pip install ruff

激活环境

虚拟环境可以"激活",使其中的包对当前 shell 可见:

=== "macOS 和 Linux"

```console
$ source .venv/bin/activate
```

=== "Windows"

```pwsh-session
PS> .venv\Scripts\activate
```

默认的 Unix 激活脚本面向 POSIX 兼容 shell(shbashzsh)。仓库中为常见替代 shell 都提供了对应脚本(对应 uv-virtualenv crate 内嵌的激活脚本模板):

  • fish$ source .venv/bin/activate.fish
  • csh / tcsh$ source .venv/bin/activate.csh
  • Nushell$ use .venv\Scripts\activate.nu

退出虚拟环境则使用 deactivate

$ deactivate

使用任意 Python 环境

uv 本身不依赖 Python 运行,因此它完全可以操作"别的 Python"的环境,这是 uv pip 接口非常实用的一个特性:

  • VIRTUAL_ENV 环境变量:设置 VIRTUAL_ENV=/path/to/venv 后,uv 会直接向 /path/to/venv 安装,与 uv 自身安装在哪里无关。注意:如果该目录不是符合 PEP 405 规范的虚拟环境,该变量会被忽略。
  • --python 选项:可以安装到任意环境,甚至是非虚拟环境。uv pip install --python /path/to/python 会安装到该解释器关联的环境中;--python 也接受虚拟环境根目录的路径。
  • --system 选项uv pip install --system 安装到系统 Python 环境,大致等价于 uv pip install --python $(which python)(但会跳过链接到虚拟环境的可执行文件)。虽然官方仍推荐虚拟环境,但 --system 适用于 CI 和容器场景。

--system 标志还有一个语义:它是"允许修改系统(非虚拟)环境"的显式开关。例如你用 --python 3.12 请求一个 Python 版本时,uv 会搜索满足请求的解释器;若找到的是系统解释器(如 /usr/lib/python3.12),必须同时提供 --system 才允许修改它,否则 uv 会忽略所有非虚拟环境的解释器。反过来,提供了 --system 时,uv 会忽略所有虚拟环境内的解释器。

官方也坦承:跨平台、跨发行版向系统 Python 安装包 notoriously 困难,uv 支持常见情况但不保证覆盖所有场景——例如 Python 3.10 之前的 Debian 系统 Python 因发行版补丁了 distutils(却没有同步补丁 sysconfig)而不被支持。在这些非标准环境中,虚拟环境是硬性要求。

另外注意:如果 uv 本身是通过 pip 安装在某个 Python 环境里的,它依然可以修改其他环境;但用 python -m uv 方式调用时,uv 会默认使用父解释器所在的环境。通过 Python 调用 uv 会增加启动开销,不推荐日常使用。uv 自身不依赖 Python,但它在(1)向环境安装依赖和(2)构建源分发包(sdist)这两个环节需要定位到一个 Python 环境。

环境发现顺序

执行 uv pip syncuv pip install 这类会修改环境的命令时,uv 按以下顺序搜索虚拟环境(见 environments 文档):

  1. VIRTUAL_ENV 环境变量标识的已激活虚拟环境;
  2. CONDA_PREFIX 标识的已激活 Conda 环境;
  3. 当前目录或最近的父目录中的 .venv(即使未激活)。

如果三者都找不到,uv 会提示你在当前目录用 uv venv 创建一个。带 --system 标志时跳过虚拟环境搜索;对于不修改环境的命令(如 uv pip compile),uv 不要求虚拟环境存在,但仍需要一个 Python 解释器(发现机制见 Python 版本发现)。

安装与管理包

安装:从包名、版本到 Git 仓库

基础安装:

$ uv pip install flask                        # 按名称安装
$ uv pip install "flask[dotenv]"              # 启用可选依赖 extra
$ uv pip install flask ruff                   # 一次安装多个
$ uv pip install 'ruff>=0.2.0'                # 带版本约束
$ uv pip install 'ruff==0.3.0'                # 固定版本

非注册表来源同样支持,且写法与 pip 一致:

$ uv pip install "ruff @ ./projects/ruff"                 # 本地目录
$ uv pip install "git+https://github.com/astral-sh/ruff"  # Git 仓库

Git 依赖可以精确到 tag、commit 或分支:

$ uv pip install "git+https://github.com/astral-sh/ruff@v0.2.0"                  # tag
$ uv pip install "git+https://github.com/astral-sh/ruff@1fadefa67b26508cc59cf38e6130bde2243c929d"  # commit
$ uv pip install "git+https://github.com/astral-sh/ruff@main"                   # 分支

私有仓库的认证方式见 Git 认证文档

可编辑(editable)安装

可编辑包在源码修改后无需重新安装即可生效:

$ uv pip install -e .                        # 安装当前项目为可编辑包
$ uv pip install -e "ruff @ ./project/ruff"  # 安装其他目录的项目

从文件批量安装

uv pip install 支持从标准文件格式批量安装:

$ uv pip install -r requirements.txt                      # requirements.txt
$ uv pip install -r pyproject.toml                         # pyproject.toml
$ uv pip install -r pyproject.toml --extra foo             # 启用 "foo" extra
$ uv pip install -r pyproject.toml --all-extras           # 启用全部 extra

依赖组(dependency groups)也可以直接安装:

$ uv pip install --group foo                                # 当前目录 pyproject.toml 中的组
$ uv pip install --project some/path/ --group foo --group bar  # 指定项目目录
$ uv pip install --group some/path/pyproject.toml:foo --group other/pyproject.toml:bar

与 pip 语义一致的一个注意点:--group 标志不会作用于 -r / -e 指定的其他来源。例如 uv pip install -r some/path/pyproject.toml --group foo 中的 foo 取自 ./pyproject.toml而不是 some/path/pyproject.toml

卸载

$ uv pip uninstall flask      # 卸载单个包
$ uv pip uninstall flask ruff # 一次卸载多个

检查环境与包

环境检查对应 inspection 文档,共四类命令:

列出已安装包

$ uv pip list             # 表格形式列出全部包
$ uv pip list --format json  # JSON 输出,便于脚本处理
$ uv pip freeze           # 以 requirements.txt 格式列出

查看包详情

$ uv pip show numpy       # 支持同时传入多个包

校验环境一致性。分多次安装可能装入互相冲突的依赖,用 uv pip check 检查冲突与缺失依赖:

$ uv pip check

兼容指南 可以看到 uv pip check 当前会报告五类诊断:包缺少 METADATA 文件或无法解析、Requires-Python 与当前解释器不匹配、依赖缺失、依赖版本不兼容、同一包在环境中存在多个版本。其中"同包多版本"是 pip check 不会报而 uv 会报的。

uv pip tree 还可以以树状图展示环境内的依赖关系(见 PipCommand 定义),适合排查复杂依赖链。

声明依赖

最佳实践是把依赖声明在静态文件里,而不是对环境做即兴安装;声明之后即可用 uv pip compile 锁定,得到一致、可复现的环境(见 declaring dependencies)。

使用 pyproject.toml

pyproject.toml 是 Python 项目定义依赖的标准载体:

[project]
dependencies = [
  "httpx",
  "ruff>=0.3.0"
]

可选依赖(extras):

[project.optional-dependencies]
cli = [
  "rich",
  "click",
]

每个 key 定义一个 extra,可通过 --extra / --all-extras 标志或 package[<extra>] 语法安装。

使用 requirements.in

轻量级的 requirements 文件格式也是常用声明方式,每个需求占一行。习惯上命名为 requirements.in,与锁定产物 requirements.txt 区分:

httpx
ruff>=0.3.0

注意该格式不支持可选依赖组。

锁定与同步环境

锁定的含义是:把 ruff 这样的依赖解析为一个精确版本写入文件,使环境可复现。不锁定的话,依赖版本会随时间、工具或平台变化。

锁定 requirements

uv pip compile 支持多种输入格式(见 locking environments):

$ uv pip compile pyproject.toml -o requirements.txt        # 标准输入来源
$ uv pip compile requirements.in -o requirements.txt
$ uv pip compile pyproject.toml requirements-dev.in -o requirements-dev.txt  # 多文件
$ uv pip compile setup.py -o requirements.txt             # 兼容遗留 setup.py / setup.cfg
$ echo "ruff" | uv pip compile -                          # 从 stdin 读取(用 -)

注意:默认情况下 uv pip compile 的输出只是打印到终端,必须用 --output-file / -o 才写入文件——这是与 pip-tools 的默认行为差异之一。

extras 与依赖组:

$ uv pip compile pyproject.toml --extra foo       # 启用 "foo" extra
$ uv pip compile pyproject.toml --all-extras     # 全部 extra
$ uv pip compile --group foo                       # 锁定当前目录的依赖组
$ uv pip compile --project some/path/ --group foo --group bar
$ uv pip compile --group some/path/pyproject.toml:foo --group other/pyproject.toml:bar

requirements.in 格式不支持 extras;--group 是 pip-tools 尚未实现的扩展(uv 先行支持)。--group 同样只作用于默认项目目录,不作用于其他显式指定的来源文件。

升级已锁定的依赖

使用输出文件时,uv 会尊重输出文件中已钉住的版本——已钉住的依赖在后续 compile 中不会被升级:

$ echo "ruff==0.3.0" > requirements.txt
$ echo "ruff" | uv pip compile - -o requirements.txt
# 结果仍是 ruff==0.3.0

需要升级时用:

$ uv pip compile - -o requirements.txt --upgrade-package ruff  # 升级单个包
$ uv pip compile - -o requirements.txt --upgrade                # 升级全部

同步环境:uv pip installuv pip sync 的区别

依赖可以直接从定义文件或编译出的 requirements.txtuv pip install 安装。但要理解一个关键区别:uv pip install 不会移除环境中已有的、锁文件里没有声明的包(除非它们与锁文件冲突),这对可复现性并不理想。若要保证环境与锁文件严格一致,应使用 uv pip sync——它会把锁文件中未列出的包从环境中移除:

$ uv pip sync requirements.txt  # 与 requirements.txt 精确同步
$ uv pip sync pylock.toml       # 也支持 PEP 751 的 pylock.toml

从 CLI 的源码注释也能印证这一语义:Sync 子命令的帮助文本 明确写着"同步时会移除文件中未列出的包;想保留多余包请改用 uv pip install",并支持 --strict 在文件中缺少传递依赖时给出警告。

约束文件(constraints)

约束文件是 requirements.txt 风格文件,但只控制需求的版本,不会触发安装。典型用途:给"不是本项目直接依赖"的包加上版本边界:

pydantic<2.0
$ uv pip compile requirements.in --constraint constraints.txt

支持每文件多条约束、多个约束文件。uv 还会读取工作区根 pyproject.toml 中的 constraint-dependencies 字段并追加到命令行指定的约束之上。

构建约束(build constraints)

与 constraints 类似,但专门针对构建期依赖(包括构建运行时依赖所需的构建依赖)。把包写进构建约束文件不会让它被安装;约束只在该包作为直接或传递的构建期依赖出现时生效。例如统一工作区内所有包使用的 setuptools 版本:

[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"
setuptools==75.0.0

uv 同样会读取工作区根 pyproject.tomlbuild-constraint-dependencies 并追加进去。

覆盖(overrides)

overrides 与 constraints 的本质区别:constraints 是加法的(与各包声明的需求求交集),overrides 是绝对的(完全替换各包声明的需求,即使这会产生"非法"的解)。最常见用途是移除传递依赖的上界。例如 a 要求 c>=1.0,<2.0b 要求 c>=2.0,二者不可兼得;用 override 强制 c>=2.0 后即可解析成功(但注意若 a 确实不兼容 c>=2.0,运行时会出问题):

c>=2.0
$ uv pip compile requirements.in --override overrides.txt

支持每文件多条 override、多个 override 文件。

与 pip 的关键行为差异

以下要点摘自 兼容指南,是切换前必须了解的差异:

  1. 不读 pip 的配置与变量。uv 不读 pip.conf、不读 PIP_INDEX_URL 等 pip 专属配置,取而代之的是自己的 UV_INDEX_URL 等环境变量以及 uv.toml / pyproject.toml[tool.uv.pip] 段。
  2. 虚拟环境是默认值uv pip install / uv pip sync 总是安装到已激活虚拟环境或搜索到的 .venv;pip 则相反,无活动环境时装到全局。uv 要求显式 --python--system 才能碰非虚拟环境——默认值被刻意反转。
  3. 多索引策略更安全。uv 默认采用 first-index 策略:按顺序搜索索引,在第一个包含该包的索引处停止,候选版本只取该索引的,以此防范 dependency confusion 攻击(pip 会合并所有索引的候选版本)。可用 --index-strategy / UV_INDEX_STRATEGY 切换 unsafe-first-matchunsafe-best-match(后者最接近 pip,但有安全风险)。
  4. PEP 517 构建隔离默认开启。包因构建依赖缺失而装不上时,可先预装构建依赖再用 --no-build-isolationuv pip install wheel && uv pip install --no-build-isolation biopython==1.77
  5. pip check--user--only-binary 等存在语义差异。uv 不支持 --user(推荐虚拟环境);--only-binary 在 uv 中对直接 URL 依赖也强制(pip 不强制),但 editable 安装两者都放行。
  6. 默认不编译字节码。pip 安装时会生成 __pycache__,uv 默认不生成,可用 --compile-bytecodeUV_COMPILE_BYTECODE=1 开启(例如 Docker 构建中建议开启以提升启动速度)。
  7. pip compile 默认差异。uv 默认不写输出文件(必须 -o)、默认剥离 extras(--strip-extras,与 pip-tools 即将变更的默认值对齐)、默认不在输出中写索引 URL(用 --emit-index-url 开启)。
  8. 预发布版本策略。默认 if-necessary:优先稳定版,仅当所有满足约束的稳定候选都被拒绝时才回退到预发布;--prerelease allow / disallow / explicit 可切换策略。
  9. 更严格、更规范。uv 往往比 pip 严格——拒绝文件名与元数据不一致的 wheel(可用 UV_SKIP_WHEEL_FILENAME_CHECK=1 放宽)、拒绝非法 URL fragment 的 HTML 索引等;包名默认按 PEP 503 归一化输出(docstring-parser 而非 docstring_parser)。

源码结构速览

想在源码层面继续深入时,入口非常清晰:

小结

uv pip 是 uv 中面向存量 pip/pip-tools 工作流的"直译层":命令名、参数习惯与 pip 高度对齐,但默认行为更安全(强制虚拟环境、first-index 索引策略、PEP 517 隔离构建)。日常工作流可以概括为三步——uv venv 建环境、uv pip compile 锁版本、uv pip sync 精确同步环境;临时探索则直接用 uv pip install / uv pip uninstall,并用 uv pip check 兜底校验环境一致性。只要记住"它不依赖 pip、也不是 pip 的逐字复刻",绝大多数 pip install 替换成 uv pip install 即可直接工作。

登录后查看全文
热门项目推荐
相关项目推荐