项目与包管理
在「环境与运行」「类型提示与虚拟环境」「多版本管理」的基础上,本篇讲 依赖怎么声明、锁定、复现。
先说结论:很多时候工具链不是你说了算的。 接手仓库、进团队、对接 CI/镜像时,正确做法是先识别现有方案并遵循,而不是强行换成自己喜欢的工具。只有绿场新项目、或团队明确要迁移时,才谈「推荐栈」。
先认现场:看仓库里有什么
克隆项目后,用这些文件判断「它属于哪套方案」:
| 标志文 件 | 常见方案 | 你通常该怎么做 |
|---|---|---|
requirements.txt(可能还有 -dev / base 等) | 经典 pip | python -m venv → pip install -r ... |
requirements.in + 生成的 requirements.txt | pip-tools | 改 .in,用 pip-compile 生成 .txt,再 pip-sync |
Pipfile + Pipfile.lock | Pipenv | pipenv install / pipenv shell |
poetry.lock + pyproject.toml(含 [tool.poetry]) | Poetry | poetry install / poetry add / poetry run |
pdm.lock | PDM | pdm install / pdm add |
uv.lock + pyproject.toml | uv | uv sync / uv add / uv run |
.python-version | 解释器版本钉扎(pyenv / uv 等) | 先装对 Python,再装依赖;见「多版本管理」 |
Dockerfile / compose.yaml | 容器构建与本地编排 | 跟现有文件与 CI;见「Docker 基础」 |
environment.yml / conda-lock.yml | Conda / Mamba | conda env create -f ...(常见于数据科学) |
仅有 setup.py / setup.cfg / 旧式 pyproject.toml | 可安装包 / 源码布局 | 按 README:pip install -e . 等 |
| 多种清单并存且文档矛盾 | 历史包袱 | 先读 README / CONTRIBUTING / CI 配置,以流水线为准 |
实操顺序建议:
- 读
README、CONTRIBUTING、.github/workflows(或同类 CI) - 按文档装环境;文档缺失则以 CI 实际执行的命令 为准
- 改依赖时用 同一套工具 更新声明与锁文件,并跑通测试
- 不要私自引入第二套锁文件「并存」——除非团队在做有计划的迁移
经典方案一览(你会反复遇到)
下列都是业界常见、合理的选择;「经典」不等于「过时」。
1. pip + requirements.txt
最常见、教程最多。
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
# 有开发依赖时常另有:
pip install -r requirements-dev.txt
特点:
- 上手简单;文件格式到处都认
- 容易只钉直接依赖、不锁传递依赖 → 换机器/过几个月可能装出不同树
- 多人协作时要约定:是手写精确
==,还是只写范围
适合:小脚本、遗留仓库、对工具零额外依赖的环境。
2. pip-tools(requirements.in → requirements.txt)
在 pip 生态里补「锁版本」:
requirements.in # 人手维护的直接依赖
requirements.txt # pip-compile 生成的完整锁定树(应提交)
pip-compile requirements.in
pip-sync requirements.txt
特点:仍是 pip 文件格式,CI/镜像改动小;比「裸 requirements」更可复现。
3. Poetry
一体化:依赖、虚拟环境、打包发布。
poetry install
poetry add requests
poetry add --group dev pytest
poetry run pytest
标志:poetry.lock、pyproject.toml 里的 [tool.poetry]。
特点:生态成熟、文档多;学习成本高于裸 pip。进 Poetry 仓库就用 Poetry 命令,别混 pip install 改环境却不改 lock。
4. Pipenv
早期流行的 Pipfile / Pipfile.lock 方案。
pipenv install
pipenv install requests
pipenv shell
现仍见于不少存量项目。接手就按 Pipenv 文档走;新项目更少从零选它。
5. PDM / Hatch 等(以 pyproject.toml 为中心)
现代打包标准围绕 PEP 621 的 [project] 表。PDM 等提供安装与锁文件;Hatch 偏项目模板与构建。
看到 pdm.lock 或 [tool.hatch] 时,跟仓库选定的工具,不要假设「有 pyproject.toml = 一定是 uv/Poetry」。
6. Conda / Mamba(及 conda-lock)
常见于数据科学、需要非 Python 二进制依赖(CUDA、系统库)的场景。
conda env create -f environment.yml
conda activate myenv
特点:
- 能管 Python 之外 的包;与纯 PyPI 流不同
- 与
pip混用要谨慎(官方也提醒顺序与来源问题) - 纯 Web/后端服务更常见 PyPI 系工具;DS/ML 仓库则 Conda 很常见
7. uv(本站绿场默认推荐)
见下文「你能做主时」。兼容接口(uv pip)也可嵌入上述多种存量流程,用于加速,而不必立刻改项目结构。
你不能做主时:原则
- 跟随仓库约定 高于个人偏好
- CI / 部署脚本 是真相来源;本地实验可以玩,合并请求要符合现有工具链
- 一种清单、一种锁文件;避免
poetry.lock与手改requirements.txt各写各的 - 需要提速时:可在 不改声明格式 的前提下用更快安 装器(例如对
requirements.txt用uv pip install -r),但加依赖仍按原工具更新文件 - 想换工具:开 RFC/提案,评估 CI、Docker、文档、同事熟练度;不要在业务紧急修复里夹带迁移
存量项目里「加一个依赖」检查表
- 确认当前方案(上表)
- 用该方案的官方命令添加(如
poetry add/ 改.in再 compile /uv add) - 确认锁文件或 requirements 的 diff 在预期内
- 本地测试 + 看 CI
- 若存在
constraints.txt/ 私有源 / 公司 mirror,按 README 保留相关参数
你能做主时:绿场推荐(uv)
新项目、或团队已决定迁移 时,本站推荐:
| 项 | 推荐 | 说明 |
|---|---|---|
| 工具 | uv | 环境 + 依赖 + 锁文件 + 运行 |
| 声明 | pyproject.toml | [project] + dependency-groups |
| 锁定 | uv.lock | 应用项目应提交 |
| 环境 | .venv/ | 不提交 |
| 运行 | uv run ... | 少依赖手动 activate |
uv init my-app && cd my-app
uv add requests
uv add --dev pytest ruff
uv run pytest
他人克隆后:uv sync → uv run ...。
pyproject.toml 与锁文件
[project]
name = "my-app"
version = "0.1.0"
requires-python = ">=3.10"
dependencies = [
"requests>=2.31",
]
[dependency-groups]
dev = [
"pytest>=8",
"ruff>=0.6",
]
uv add 'requests>=2.31'
uv add --dev pytest
uv remove requests
uv lock
uv sync
uv sync --locked --no-dev # 生产/镜像常见写法
| 文件 | 是否提交 | 原因 |
|---|---|---|
pyproject.toml | 是 | 声明与元数据 |
uv.lock | 应用:是 | 可复现 |
.venv/ | 否 | 本机产物 |
版本约束:直接依赖写合理范围;整棵树交给锁文件。不要只在环境里 pip install 却不改声明。
方案怎么选(决策简表)
| 情境 | 更常见的选择 |
|---|---|
| 公司已有 Poetry/pip-tools/Conda 规范 | 跟规范 |
| 绿场应用 / 脚本服务,想要快、少工具 | uv |
| 必须最大兼容「只会 pip」的同事/环境 | pip + requirements,或 pip-tools 锁版本 |
| 需要发布到 PyPI 的库 | pyproject.toml(PEP 621)+ 团队选定的构建后端;锁文件策略跟团队 |
| 强依赖系统库 / CUDA 等 | Conda/Mamba 或容器镜像层解决,而不是硬套纯 pip |
遗留 setup.py 为主 | 先 pip install -e ".[dev]" 能跑;迁移打包格式另开专项 |
没有放之四海的唯一正确答案;一致性与可复现 比品牌更重要。
迁移(有计划时才做)
从经典方案迁到 uv(或 反过来)应单独排期,典型路径:
pip / pip-tools → uv(渐进)
- 安装仍用
uv pip install -r requirements.txt(或uv pip sync),声明格式暂时不动 - 再
uv add -r requirements.in -c requirements.txt(若有 in/txt 分工)生成pyproject.toml+uv.lock - 改 CI / Docker / README,删掉双轨维护
Poetry 等 → uv
- 导出或改写依赖到
[project],生成uv.lock,逐项对齐可选依赖与脚本入口 - 并行跑一段时间 CI,确认解析结果与测试通过后再切默认工具
不要做的事
- 业务 hotfix 里顺便换包管理器
- 锁文件格式换了却不改部署文档
- 仓库里长期保留两套互相漂移的依赖真相源
需要把锁导出给只认 requirements 的下游时,uv 可:uv export --format requirements.txt。
镜像内如何按锁文件安装、多阶段构建等,见工程实践「Docker 基础」。
跨方案都成立的实践
无论 pip、Poetry 还是 uv:
- 一项目一隔离环境,不污染系统 Python
- 声明文件与锁文件是真相;环境是派生物
- 开发依赖与运行时依赖分开(dev requirements / Poetry group / dependency-groups)
- CI 与生产按锁文件(或等价钉死集合)安装,避免「本机漂、线上炸」
- 解释器版本与
requires-python/ CI 一致(详见「多版本管理」) - 私有源、镜像、证书 写进文档与 CI,而不是只在某台电脑的 shell 历史里
- 改依赖的 MR 应包含:声明 diff + 锁文件 diff + 简短说明
要点
- 存量项目:先识别、再遵循;CI 与 README 优先于个人偏好
- 经典方案(pip / pip-tools / Poetry / Pipenv / Conda / PDM…)都会长期存在,要会认、会跟
- 绿场 本站默认推荐 uv +
pyproject.toml+uv.lock - 迁移是专项,不是顺手事;过渡期避免双轨真相源
- 跨工具共通目标:可复现、可协作、声明与安装一致(含 Python 版本)