Skip to main content

项目与包管理

在「环境与运行」「类型提示与虚拟环境」「多版本管理」的基础上,本篇讲 依赖怎么声明、锁定、复现

先说结论:很多时候工具链不是你说了算的。 接手仓库、进团队、对接 CI/镜像时,正确做法是先识别现有方案并遵循,而不是强行换成自己喜欢的工具。只有绿场新项目、或团队明确要迁移时,才谈「推荐栈」。

先认现场:看仓库里有什么

克隆项目后,用这些文件判断「它属于哪套方案」:

标志文件常见方案你通常该怎么做
requirements.txt(可能还有 -dev / base 等)经典 pippython -m venvpip install -r ...
requirements.in + 生成的 requirements.txtpip-tools.in,用 pip-compile 生成 .txt,再 pip-sync
Pipfile + Pipfile.lockPipenvpipenv install / pipenv shell
poetry.lock + pyproject.toml(含 [tool.poetry]Poetrypoetry install / poetry add / poetry run
pdm.lockPDMpdm install / pdm add
uv.lock + pyproject.tomluvuv sync / uv add / uv run
.python-version解释器版本钉扎(pyenv / uv 等)先装对 Python,再装依赖;见「多版本管理
Dockerfile / compose.yaml容器构建与本地编排跟现有文件与 CI;见「Docker 基础
environment.yml / conda-lock.ymlConda / Mambaconda env create -f ...(常见于数据科学)
仅有 setup.py / setup.cfg / 旧式 pyproject.toml可安装包 / 源码布局按 README:pip install -e .
多种清单并存且文档矛盾历史包袱先读 README / CONTRIBUTING / CI 配置,以流水线为准

实操顺序建议:

  1. READMECONTRIBUTING.github/workflows(或同类 CI)
  2. 按文档装环境;文档缺失则以 CI 实际执行的命令 为准
  3. 改依赖时用 同一套工具 更新声明与锁文件,并跑通测试
  4. 不要私自引入第二套锁文件「并存」——除非团队在做有计划的迁移

经典方案一览(你会反复遇到)

下列都是业界常见、合理的选择;「经典」不等于「过时」。

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.lockpyproject.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)也可嵌入上述多种存量流程,用于加速,而不必立刻改项目结构。

你不能做主时:原则

  1. 跟随仓库约定 高于个人偏好
  2. CI / 部署脚本 是真相来源;本地实验可以玩,合并请求要符合现有工具链
  3. 一种清单、一种锁文件;避免 poetry.lock 与手改 requirements.txt 各写各的
  4. 需要提速时:可在 不改声明格式 的前提下用更快安装器(例如对 requirements.txtuv pip install -r),但加依赖仍按原工具更新文件
  5. 想换工具:开 RFC/提案,评估 CI、Docker、文档、同事熟练度;不要在业务紧急修复里夹带迁移

存量项目里「加一个依赖」检查表

  1. 确认当前方案(上表)
  2. 用该方案的官方命令添加(如 poetry add / 改 .in 再 compile / uv add
  3. 确认锁文件或 requirements 的 diff 在预期内
  4. 本地测试 + 看 CI
  5. 若存在 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 syncuv 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(渐进)

  1. 安装仍用 uv pip install -r requirements.txt(或 uv pip sync),声明格式暂时不动
  2. uv add -r requirements.in -c requirements.txt(若有 in/txt 分工)生成 pyproject.toml + uv.lock
  3. 改 CI / Docker / README,删掉双轨维护

Poetry 等 → uv

  • 导出或改写依赖到 [project],生成 uv.lock,逐项对齐可选依赖与脚本入口
  • 并行跑一段时间 CI,确认解析结果与测试通过后再切默认工具

不要做的事

  • 业务 hotfix 里顺便换包管理器
  • 锁文件格式换了却不改部署文档
  • 仓库里长期保留两套互相漂移的依赖真相源

需要把锁导出给只认 requirements 的下游时,uv 可:uv export --format requirements.txt

镜像内如何按锁文件安装、多阶段构建等,见工程实践「Docker 基础」。

跨方案都成立的实践

无论 pip、Poetry 还是 uv:

  1. 一项目一隔离环境,不污染系统 Python
  2. 声明文件与锁文件是真相;环境是派生物
  3. 开发依赖与运行时依赖分开(dev requirements / Poetry group / dependency-groups)
  4. CI 与生产按锁文件(或等价钉死集合)安装,避免「本机漂、线上炸」
  5. 解释器版本与 requires-python / CI 一致(详见「多版本管理」)
  6. 私有源、镜像、证书 写进文档与 CI,而不是只在某台电脑的 shell 历史里
  7. 改依赖的 MR 应包含:声明 diff + 锁文件 diff + 简短说明

要点

  1. 存量项目:先识别、再遵循;CI 与 README 优先于个人偏好
  2. 经典方案(pip / pip-tools / Poetry / Pipenv / Conda / PDM…)都会长期存在,要会认、会跟
  3. 绿场 本站默认推荐 uv + pyproject.toml + uv.lock
  4. 迁移是专项,不是顺手事;过渡期避免双轨真相源
  5. 跨工具共通目标:可复现、可协作、声明与安装一致(含 Python 版本)