Python 环境工具全景:venv/uv/poetry/conda 怎么选
以「接手一个工具各异的团队仓库」为场景,横向看清 venv、virtualenv、pip、poetry、conda、uv 的分工与取舍:靠锁文件识别项目工具链、各工具核心命令、pip 与 uv 的数量级差距、conda 管非 Python 依赖的独特价值,附新项目决策清单与跨语言对照。
前置知识
- 已读完 虚拟环境:会创建与激活
.venv,知道 requirements.txt 的角色; - 最好也翻过 pyenv 与 uv 版本管理:知道「解释器版本」与「依赖」是两层问题。
你现在要解决什么问题
你入职第一天拉下团队仓库,发现三个项目三副面孔:老脚本是 requirements.txt + venv;后端服务用 poetry(pyproject.toml + poetry.lock);新写的工具用 uv(uv.lock)。同事 A 说「都 2026 年了直接 uv」,同事 B 说「数据科学那套还是 conda 稳」。你没跑过这些工具,根本插不上话。
这篇不教你再装一遍环境(040 已教),只解决三件事:拿到一个陌生项目,怎么一眼看出它用哪套工具;每套工具的最小命令集长什么样;新项目你自己该选哪个。
第一步:靠文件指纹认出工具链
不用问人,看仓库根目录的文件组合:
| 你看到 | 它在说 | 进场命令 |
|---|---|---|
只有 requirements.txt | 传统 pip + venv 流 | python -m venv .venv 激活后 pip install -r requirements.txt |
pyproject.toml + poetry.lock | poetry 项目 | poetry install,日常 poetry add / poetry run |
pyproject.toml + uv.lock | uv 项目 | uv sync,日常 uv add / uv run |
pyproject.toml + uv.lock + .python-version | uv 全家桶(连解释器也管) | uv sync 会自动装对版本的 Python |
environment.yml | conda 项目(多见于数据科学) | conda env create -f environment.yml |
只有散落的 .py | 个人小脚本 | 建议你按 040 自己补一个 venv |
原则一条:装依赖之前先认锁文件,跟着锁文件走。在有 uv.lock 的项目里手滑 pip install,会把环境弄成与锁文件不一致的状态,同事拉代码后必炸。
工具全景表:谁在哪个位置
历史脉络一句话:venv(2012 进标准库)解决隔离;pip 解决装包;两者都不管「依赖冲突的求解」与「锁文件」,于是 poetry(2018)把「项目 + 依赖解析 + 锁定」打包;uv(2024,Rust 实现)把这一整条链重写了一遍,速度拉开数量级,2025 年起成为新项目默认答案。conda 是另一条独立路线,下面单说。
| 工具 | 隔离 | 依赖解析 | 锁文件 | 速度 | 管 Python 版本 | 管 C/CUDA 等非 Python 依赖 |
|---|---|---|---|---|---|---|
| venv + pip | 是 | 无(pip 只管装) | 无(手写 requirements) | 基准 | 否 | 否 |
| virtualenv | 是 | 无 | 无 | 较快 | 可指定任意版本创建 | 否 |
| poetry | 是 | 是(resolvelib) | poetry.lock | 慢 | 弱(配 pyenv) | 否 |
| conda / pixi | 是 | 是(SAT / PubGrub) | environment.yml / 锁 | 慢 / 快 | 是 | 是(独门) |
| uv | 是 | 是(PubGrub) | uv.lock | 极快 | 是(自动下载) | 否 |
两个容易被追问的点:
- virtualenv 还有人用吗:它是 venv 的「前辈 + 增强版」,创建更快、能指定任意解释器建环境;venv 进标准库后,多数场景被 venv 取代,需要跨版本建环境时仍会碰到它。
- rye / pdm / hatch 呢:同代项目管理器,uv 出现后生态快速收敛(rye 团队已并入 uv 项目)。知道名字即可,不必投入。
速度差距是真实的:一次冷装实测
从空环境装同一组常用包(fastapi、sqlalchemy、pytest 等约 50 个传递依赖),数量级感受一下:
pip 冷装约 18-22 秒 热缓存约 12-16 秒
poetry 冷装约 29 秒 热缓存约 20 秒
uv 冷装约 1.2 秒 热缓存约 0.3 秒差距来源不是魔法:uv 用 Rust 重写了下载、解压与解析(并行 + 全局缓存 + PubGrub 求解算法),并且全局缓存意味着第二个项目装同一批包几乎是本地复制。CI 里这个差距乘以每次构建,就是团队真实省下的时间。
conda 的独特位置:管到 Python 之外
conda 常被误当成「另一个 pip」。它真正的不可替代性在最后一列:能安装非 Python 依赖。装一个深度学习环境时,numpy 加速底层的 MKL、GPU 相关的 CUDA 运行库、编译工具链,这些不是 Python 包,pip 装不了或装得半身不遂;conda(及 Rust 重写的后继者 pixi)把它们和 Python 包一起求解、一起装好。所以:
- 纯 Python 项目:用不着 conda,它反而更慢更重;
- 数据科学 / 深度学习 / 科学计算:conda 系仍是省心解,尤其要特定 CUDA 版本时。
各工具最小命令集
poetry(认准「声明在 pyproject、锁在 poetry.lock、跑在 poetry run 里」):
poetry init # 交互式生成 pyproject.toml(老项目接入)
poetry new mypkg # 新建标准项目骨架
poetry add fastapi # 安装并写入依赖
poetry add pytest --group dev # 开发依赖分组
poetry install # 按锁文件精确安装
poetry env use python3.12 # 指定解释器建环境
poetry run python main.py # 在项目环境里运行
poetry build && poetry publish # 构建 sdist/wheel 并发 PyPIconda(环境在 conda 眼里是「有名字的」):
conda create -n dl python=3.12 # 建名为 dl 的环境
conda activate dl # 激活(注意不是 source)
conda env create -f environment.yml # 按文件重建
conda install pytorch torchvision # 装 conda 仓库的包(含二进制依赖)
conda deactivateuv(命令在 030 与 040 已系统学过,这里只列项目管理姿势):
uv init myproject # 生成 pyproject.toml 骨架
uv add fastapi # 声明并安装,锁进 uv.lock
uv sync # 按锁文件精确重建(会自动装 .python-version 指定的解释器)
uv run python main.py # 环境内运行,无需手动激活
uv run --with httpx python -c "import httpx" # 临时依赖跑一次性任务动手实验:把 040 的 venv-lab 项目用 uv init 重做一遍,对比生成的 pyproject.toml 与你手写的 requirements.txt——前者声明「直接依赖」,锁文件记「全部精确版本」,分工更清楚。
新项目怎么选:一张决策卡
按顺序问自己:
- 要装 CUDA / MKL 这类非 Python 依赖吗?要,conda 或 pixi;不要,继续;
- 是一个人的小脚本?venv + pip 足够,别为脚本引入工具链;
- 是要长期维护、多人协作、要发布的项目?uv(2026 年新项目的默认答案:快、锁文件、连解释器一起管、monorepo 友好);
- 团队已经在用 poetry?跟着团队,工具一致性大于工具先进性。想提速可以只把安装环节换成
uv pip install(读 poetry.lock 之外的场景慎用,别破坏锁状态)。
迁移提示:poetry 转 uv 有成熟的 uv sync 兼容路径,但不要在项目半路换工具而不通知团队——锁文件是协作契约。
跨语言视角:Python 的 venv 是特例吗
| 语言 | 包管理 | 隔离方式 | 锁文件 |
|---|---|---|---|
| Node.js | npm / pnpm | node_modules(项目本地) | package-lock.json |
| Rust | cargo | 自动(编译期隔离) | Cargo.lock |
| Go | go mod | 模块路径隔离 | go.sum |
| Python | pip / uv / poetry | 显式虚拟环境 | requirements / *.lock |
多数现代语言靠「项目本地依赖目录 + 全局缓存」天然隔离,不需要你显式「激活」什么。uv 正在把 Python 往 cargo 这个体感拉近(自动管理环境、无感切换),但只要团队里还有 pip 用户,显式虚拟环境仍是 Python 的通用语。理解了这一点,你就明白为什么本文有这张表,而 Go 的教程没有。
自我检查
- 拿到陌生仓库,能凭文件组合说出工具链与进场命令;
- 能说出 uv 快的三个来源(Rust 重写、全局缓存、现代求解算法);
- 能解释 conda 与 pip 的本质区别(是否管非 Python 依赖);
- 新项目能按决策卡给出选择并说明理由。
练习
预测题:在一个有 uv.lock 的项目里直接 pip install requests,uv sync 之后再 pip list,requests 还在吗?动手验证(提示:uv sync 会把环境恢复到锁文件状态)。
修改题:把一个只有 requirements.txt 的老项目接入 uv:uv init 后用 uv add -r requirements.txt 批量迁移直接依赖,观察 pyproject.toml 与 uv.lock 的生成。
排错题:同事报告「在你这个 poetry 项目里 poetry install 报 lock 过期」。为什么会发生?两个标准修法是什么(poetry lock --no-update 重新生成锁 / 提交前先 poetry install 自检)?
挑战题:为你的团队写一页 CONTRIBUTING 环境指引:新成员按工具链判断表进场,附每条命令的预期输出。写完发给一位没接触过 Python 的朋友试跑,收集卡点。
下一步
- 工具的底层机制(隔离如何实现、requirements 与锁文件的分工)回到 虚拟环境;
- 工具链是工程化的第一环,继续走 模块、包与工程化;
- uv 项目模式的完整官方文档:https://docs.astral.sh/uv/