前言
Python 的环境管理一直是开发者的痛点。你大概率经历过这些事:用
pip install装依赖等了五分钟、conda create卡在 "Solving environment" 转了十分钟、不同项目的依赖互相打架、换台电脑环境就搭不起来。 目前 Python 生态中主流的两条路线是 conda 和 uv。conda 是数据科学和 AI领域的老牌选手,统治了十多年;uv 是 2024 年崛起的挑战者,用 Rust 重写,号称快 10~100 倍,已被 OpenAI收购。理解这两个工具的设计哲学和适用场景,是每个 Python 开发者的基本功。
一、它们各自解决什么问题
pip + venv:最基础的组合
Python 自带 pip,它只做一件事------从 PyPI 下载并安装 Python 包。但 pip 不管环境隔离:你在系统 Python 里装了一堆包,所有项目共享同一个环境,版本冲突在所难免。
为了解决隔离问题,Python 提供了 venv(虚拟环境),它在项目目录下创建一套独立的 Python 解释器和 site-packages,让不同项目的依赖互不影响。
这个组合的问题在于:pip 的依赖解析很慢,不支持 lockfile,不管理 Python 版本,大型项目中经常遇到"装不上""装错了""装太慢"三连。
conda:一站式环境 + 包管理
conda 从一开始就走了不同的路。它不仅管理 Python 包,还能管理 C/C++ 库、CUDA 工具链、R 语言包等非 Python 依赖。conda 的环境隔离不是基于 venv 的复制,而是创建独立的目录树------每个环境有自己的 Python 解释器、共享库和包。
这在科学计算领域很重要:装一个 numpy 可能需要特定版本的 BLAS/LAPACK,装 pytorch 需要匹配 CUDA 版本。pip 处理不了这些底层依赖,conda 可以。
uv:用 Rust 重写一切
uv 由 Astral 公司(也是 ruff 的开发者)用 Rust 编写,目标是用一个工具替代 pip + pip-tools + virtualenv + pyenv + pipx + poetry 这整条工具链。它的核心卖点是速度------安装 100 个依赖,pip 要 8 分钟,uv 只要 15 秒。
二、uv 深度解析
2.1 为什么这么快
uv 的速度优势不是靠简单优化,而是从底层架构上就不同:
Rust 实现:零成本抽象 + 内存安全,没有 Python GIL 的限制,可以充分利用多核 CPU 并行处理依赖解析和下载。
并行下载与安装:pip 是串行下载包、串行安装。uv 同时下载所有依赖,同时解压和安装,I/O 和 CPU 不闲着。
全局缓存 + 硬链接 :uv 维护一个全局缓存目录(~/.cache/uv)。第一次下载某个包后,后续项目再需要同一个版本时,uv 不会重新下载,而是通过硬链接直接指向缓存中的文件。这意味着多个项目共享同一份包文件,既省磁盘又省时间。
解析器优化:uv 使用移植自 Dart 的 PubGrub 算法(而非 pip 的回溯算法),并针对 Python 生态做了深度定制------提前终止无效分支、缓存中间解析结果、按约束严格程度智能排序。
实测数据:安装 100 个依赖,uv 比 pip 快 10~100 倍;创建虚拟环境,uv 比 virtualenv 快 30 倍;Docker 构建时间缩短 60%~80%。
2.2 核心功能全景
uv 不只是"更快的 pip",它是一个统一的 Python 工具链:
| 功能 | uv 命令 | 替代的传统工具 |
|---|---|---|
| 安装包 | uv pip install <pkg> |
pip install |
| 创建虚拟环境 | uv venv |
python -m venv / virtualenv |
| 锁定依赖 | uv pip compile |
pip-compile (pip-tools) |
| 同步依赖 | uv pip sync |
pip-sync (pip-tools) |
| 安装 Python | uv python install 3.12 |
pyenv install |
| 运行脚本 | uv run script.py |
python script.py(自动激活环境) |
| 运行 CLI 工具 | uvx ruff check . |
pipx run ruff check . |
| 项目管理 | uv init / uv add / uv lock |
poetry init / poetry add |
2.3 项目管理模式
uv 原生支持 pyproject.toml 作为项目配置文件,并提供 lockfile 机制:
bash
# 初始化新项目
uv init my-project
cd my-project
# 添加依赖(自动更新 pyproject.toml 和 uv.lock)
uv add requests flask
# 锁定依赖版本(生成 uv.lock)
uv lock
# 安装所有依赖(严格按 lockfile)
uv sync
uv.lock 文件记录了每个依赖的精确版本和哈希值,确保团队所有成员和 CI 环境安装的包完全一致------这是 pip 原生做不到的。
2.4 单文件脚本依赖
uv 支持在单个 Python 文件中内嵌依赖声明:
python
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "requests",
# "rich",
# ]
# ///
import requests
from rich import print
resp = requests.get("https://api.github.com")
print(resp.json())
运行 uv run script.py,uv 会自动创建临时环境、安装依赖、执行脚本。这意味着你可以把一个完整的可运行程序打包成一个 .py 文件,分享给别人时无需附带 requirements.txt。
三、conda 深度解析
3.1 conda 的设计哲学
conda 的核心定位是跨语言的包管理和环境管理工具。它不只面向 Python------在生物信息学、地球科学等领域,conda 被用来管理 R、Julia、C/C++ 等各种语言的依赖。
conda 的包格式是 .tar.bz2 或 .conda,里面装的是预编译的二进制文件,而不是 PyPI 上的源码包(sdist/wheel)。这意味着 conda 在安装时不需要编译,直接解压就能用------省去了编译环境和编译时间的麻烦。
3.2 环境隔离机制
conda 的环境隔离比 venv 更彻底。venv 本质上是复制一份 Python 解释器的引用 ,共享系统的部分库;conda 环境是一个完全独立的目录树:
~/miniconda3/envs/myenv/
├── bin/
│ ├── python # 独立的 Python 解释器
│ ├── pip
│ └── ...
├── lib/
│ ├── python3.11/
│ │ └── site-packages/ # 独立的包目录
│ ├── libmkl_intel_lp64.so # 独立的 BLAS 库
│ └── libcuda.so # CUDA 运行时库
└── include/
└── ...
每个 conda 环境自带完整的依赖栈,包括底层 C/C++ 库。这就是为什么 conda 在安装 pytorch 时可以自动处理 CUDA 依赖------它把 CUDA toolkit 也当作一个"包"来管理。
3.3 Channel 机制
conda 的包来源于 Channel(频道),这是一种分层的包仓库:
- defaults:Anaconda 公司维护的默认频道,商业使用需要许可证
- conda-forge:社区维护的开源频道,包的数量和质量最高
- 自定义频道:企业内部可以搭建私有频道
bash
# 配置 conda-forge 为优先频道
conda config --add channels conda-forge
conda config --set channel_priority strict
conda 在安装包时会从所有已配置的 Channel 中搜索,按优先级选择。这和 pip 从 PyPI 单一源安装不同。
3.4 依赖解析:SAT Solver
conda 的依赖解析使用 SAT 求解器------把依赖关系转化成一个布尔可满足性问题(Satisfiability),用数学方法找出是否存在一个满足所有约束的安装方案。
这个过程在理论上很严谨,但实际体验是慢。原因包括:
- Channel 中的元数据量巨大(conda-forge 有上万个包,每个包有多个版本和构建变体)
- 约束条件复杂(不仅要匹配 Python 版本,还要匹配 CUDA 版本、操作系统、CPU 架构)
- SAT 求解器的搜索空间随包数量指数增长
这就是为什么 conda install 经常卡在 "Solving environment..." 上。社区为此开发了 Mamba------一个用 C++ 重写的 conda 替代品,专门优化了解析速度,效果提升明显。
3.5 常用命令
bash
# 环境管理
conda create -n myenv python=3.11 # 创建环境
conda activate myenv # 激活环境
conda deactivate # 退出环境
conda env list # 列出所有环境
conda env export > environment.yml # 导出环境
conda env create -f environment.yml # 从文件创建环境
# 包管理
conda install numpy pytorch # 安装包
conda install -c conda-forge scipy # 从指定频道安装
conda update --all # 更新所有包
conda list # 列出已安装包
conda search pytorch # 搜索可用包
# 清理
conda clean --all # 清理缓存和未使用的包
四、uv 与 conda 的全面对比
| 维度 | uv | conda |
|---|---|---|
| 实现语言 | Rust | Python |
| 包来源 | PyPI(纯 Python 生态) | conda channels(跨语言) |
| 包格式 | wheel / sdist | 预编译二进制(.tar.bz2 / .conda) |
| 环境隔离 | 基于 venv(轻量) | 独立目录树(重量) |
| Python 版本管理 | 支持(uv python install) |
支持(conda install python=3.11) |
| 非 Python 依赖 | 不支持 | 支持(CUDA、C/C++ 库等) |
| 依赖解析算法 | PubGrub(快) | SAT Solver(慢但严谨) |
| Lockfile | 原生支持(uv.lock) |
通过 environment.yml 导出 |
| 安装速度 | 极快(10~100x pip) | 慢("Solving environment...") |
| 工具体积 | ~10MB | ~400MB(Miniconda) |
| CUDA 管理 | 不直接管理 | 可以自动安装 CUDA toolkit |
关键差异解读
速度差异的本质:uv 的快不只是"Rust 比 Python 快"。它的全局缓存 + 硬链接机制意味着同一个包只需下载和存储一次,后续所有项目直接复用。而 conda 每次创建新环境都可能重新下载和安装。
包来源的差异:uv 从 PyPI 拿包,PyPI 上绝大多数是 wheel 格式(预编译),但也有 sdist(源码包)需要本地编译。conda 的包全是预编译二进制,不会出现"编译失败"的问题,但也意味着包的更新速度比 PyPI 慢(conda-forge 需要社区构建和审核)。
非 Python 依赖是分水岭:如果你需要安装的包依赖 CUDA、MKL、HDF5 等底层库,conda 是目前最省心的选择。uv 在这方面无能为力------它只能处理 PyPI 上的纯 Python 包和 wheel。
五、怎么选:场景决策框架
选 uv 的场景:
- 纯 Python 项目(Web 开发、脚本工具、API 服务)
- 追求速度和开发体验(CI/CD 构建时间敏感)
- 需要 lockfile 保证环境可重现
- 团队协作,需要统一依赖版本
- 单文件脚本分享(内嵌依赖声明)
选 conda 的场景:
- 深度学习 / 科学计算,需要管理 CUDA、cuDNN、MKL 等底层依赖
- 跨语言项目(Python + R、Python + C/C++)
- 生物信息学等领域(conda-forge 有大量专业工具)
- 需要在没有系统级安装权限的机器上独立管理完整的运行环境
混合使用的方案:
很多 AI 团队采用"conda 管底层,uv/pip 管上层"的策略:用 conda 安装 Python 解释器和 CUDA 工具链,然后用 uv pip install 安装 PyPI 上的 Python 包。这样既利用了 conda 管理非 Python 依赖的能力,又享受了 uv 的安装速度。
bash
# 混合方案示例
conda create -n myenv python=3.11 cudatoolkit=12.1
conda activate myenv
uv pip install torch torchvision # 用 uv 快速安装 PyTorch
uv pip install -r requirements.txt # 用 uv 安装其余依赖
六、常见坑与排查
坑1:conda 的 defaults 频道商业许可问题
Anaconda 的 defaults 频道从 2020 年起对商业用户收费(200 人以上的企业)。如果你在公司环境使用 conda,应该配置 conda-forge 作为唯一频道,或使用 Miniforge(默认只用 conda-forge)。
bash
conda config --remove channels defaults # 移除默认频道
conda config --add channels conda-forge # 添加社区频道
坑2:uv 和 conda 环境混用导致的问题
如果你在 conda 环境中使用 uv pip install,uv 会把包装到 conda 环境的 site-packages 中。这通常没问题,但如果 conda 和 uv 安装了同一个包的不同版本,可能会导致冲突。建议明确分工:要么全用 conda,要么 conda 只管底层 + uv 管 Python 包。
坑3:conda "Solving environment" 卡死
这是 conda 最被诟病的问题。解决方案:
- 使用 Mamba 替代 conda(
conda install mamba -n base -c conda-forge,之后用mamba install代替conda install) - 配置
conda config --set solver libmamba,启用新的高速求解器(conda 23.10+ 已内置) - 减少 channel 数量,缩小搜索空间
坑4:uv 不支持需要源码编译的包
少数 Python 包没有预编译的 wheel(特别是一些小众的科学计算包),需要从源码编译安装。uv 对这种情况的支持不如 pip 成熟。遇到时可以回退到 pip install 单独处理。
坑5:团队环境不一致
不管用 uv 还是 conda,都要提交 lockfile / environment.yml 到版本控制。没有锁定文件,"在我机器上能跑"的问题永远不会消失。
七、命令对照速查表
| 操作 | uv | conda | pip + venv |
|---|---|---|---|
| 创建环境 | uv venv |
conda create -n env |
python -m venv env |
| 激活环境 | source .venv/bin/activate |
conda activate env |
source env/bin/activate |
| 安装包 | uv pip install pkg |
conda install pkg |
pip install pkg |
| 导出依赖 | uv pip freeze |
conda env export |
pip freeze |
| 从文件安装 | uv pip sync |
conda env create -f |
pip install -r |
| 管理 Python 版本 | uv python install 3.12 |
conda install python=3.12 |
不支持 |
| 运行 CLI 工具 | uvx ruff |
conda run ruff |
pipx run ruff |
| 清理缓存 | uv cache clean |
conda clean --all |
pip cache purge |
八、总结
conda 和 uv 代表了 Python 环境管理的两种哲学。conda 走的是"大而全"路线------用一个工具管理所有语言的依赖,代价是速度和体积。uv 走的是"快而精"路线------专注 Python 生态,用 Rust 把每个环节做到极致速度,但对非 Python 依赖无能为力。
对于 AI Infra 方向的工程师来说,选择逻辑很清晰:需要 CUDA 等非 Python 依赖时用 conda(搭配 libmamba solver 或 Mamba 提速),纯 Python 项目用 uv。在 AI 团队中,两者混合使用(conda 管底层 + uv 管上层)是目前最常见的实践。
不管用哪个工具,最关键的习惯是锁定依赖版本 ------用 uv.lock 或 environment.yml 确保环境可重现。"在我机器上能跑"这个问题,本质上都是因为没有做好依赖锁定。