当前位置: 首页 > news >正文

从“能装上”到“可复现”:Python 团队如何正确使用 requirements.txt、锁定文件与依赖分组

从“能装上”到“可复现”:Python 团队如何正确使用 requirements.txt、锁定文件与依赖分组

团队 Python 项目最常见的混乱,往往不是代码写错,而是环境不一致:A 同事本地能跑,B 同事pip install -r requirements.txt后报错;CI 今天通过,明天突然失败;生产环境和开发环境看起来装的是“同一批依赖”,行为却不一样。

问题的根源在于:“能装上”只说明依赖解析器找到了一个可用组合;“可复现”要求任何人在指定时间、指定平台、指定 Python 版本下,都能安装出同一个环境。这两者之间隔着版本范围、传递依赖、平台差异、构建后端、包索引状态和锁定策略。

现代 Python 依赖管理里,可以把三类文件分清楚:

类型作用是否应该提交典型使用场景
pyproject.toml/requirements.in声明“我需要什么”人类维护的直接依赖
requirements.txtpip 安装清单,也常被当作扁平锁定结果通常是部署、CI、传统 pip 工作流
pylock.toml/uv.lock/poetry.lock记录完整解析结果可复现安装、生产部署
依赖分组区分 prod/dev/test/docs/lint本地开发、测试、文档、CI

pip 官方文档明确说,requirements 文件是给pip install使用的安装项列表,但完整语法与 pip 内部细节紧密绑定,并不是一个面向所有工具的通用标准。(pip.pypa.io) Python Packaging 规范则把依赖说明符定义为 PEP 508 风格:可以指定包名、版本范围、extras、URL、环境标记等。(Python 打包用户指南)


一、requirements.txt:不要把它误当成“万能真相”

很多团队的第一个习惯是:

pip freeze>requirements.txt pipinstall-rrequirements.txt

这在小脚本里能用,但在团队项目中很容易埋雷。pip freeze记录的是“当前环境已经装了什么”,里面可能混入临时调试工具、全局污染包、过期依赖,甚至记录了并非项目真正需要的包。

更合理的思路是:

人类维护:我直接依赖什么 ↓ 工具解析:所有直接依赖 + 传递依赖 ↓ 锁定输出:给 CI / 生产 / 新同事安装

例如先写一个最小输入文件:

# requirements.in fastapi>=0.110,<1.0 uvicorn[standard]>=0.29,<1.0 pydantic>=2,<3

再生成锁定版:

python-mpipinstallpip-tools pip-compile --generate-hashes-orequirements.txt requirements.in

生成后的requirements.txt可能类似:

fastapi==0.115.12 \ --hash=sha256:... pydantic==2.11.4 \ --hash=sha256:... starlette==0.46.2 \ --hash=sha256:... uvicorn==0.34.2 \ --hash=sha256:...

pip-tools文档说明,pip-compile可以从pyproject.tomlsetup.pysetup.cfgrequirements.in编译出锁定的requirements.txt;它还支持--generate-hashes生成哈希校验。(pip-tools.readthedocs.io)(pip-tools.readthedocs.io)

一句话原则:

requirements.in / pyproject.toml:人写 requirements.txt:工具生成

如果团队成员手改生成后的requirements.txt,后续升级、审计、回滚都会变得困难。


二、锁定文件:可复现环境的核心

“能装上”和“可复现”之间最大的差距,是依赖解析存在很多合法答案。

比如你写:

requests>=2.30

今天解析到:

requests==2.32.3 urllib3==2.2.2 certifi==2024.8.30

几个月后,可能解析到:

requests==2.33.0 urllib3==2.3.0 certifi==2025.x.x

这两次都“能装上”,但行为未必完全一致。

真正的锁定文件应该记录:

直接依赖 传递依赖 精确版本 环境标记 Python 版本约束 平台差异 包来源 文件哈希

Python Packaging 已经有pylock.toml规范,它的目标就是描述 Python 环境中可复现安装所需的依赖。(Python 打包用户指南) pip 也已有实验性的pip lock命令,但官方说明其生成结果只保证对当前 Python 版本和平台有效。(pip.pypa.io)

所以在实践中可以这样选:

项目类型推荐方案
传统 pip 项目requirements.in+pip-compile+requirements.txt
现代应用项目pyproject.toml+uv.lock/pylock.toml
Python 库pyproject.toml声明宽松范围,测试矩阵覆盖多个版本
数据科学项目锁 Python 版本、锁依赖、锁系统依赖,必要时用容器
生产服务必须提交锁定文件,CI 和部署都从锁定文件安装

一个更稳的生产安装命令:

pipinstall--require-hashes-rrequirements.txt

如果你用pip-tools,同步环境时不要只用pip install,更推荐:

pip-sync requirements.txt

因为pip install -r requirements.txt不会自动删除环境里多余的包;pip-sync会让虚拟环境精确匹配锁定文件。pip-tools文档也强调,pip-sync会安装、升级或卸载必要内容,使环境反映 requirements 文件。(pip-tools.readthedocs.io)


三、依赖分组:别让开发工具污染生产环境

团队项目通常至少有这些依赖:

生产运行:fastapi、sqlalchemy、redis 测试:pytest、coverage 代码质量:ruff、mypy 文档:mkdocs、sphinx 本地调试:ipython、debugpy

如果全部塞进一个requirements.txt,生产镜像会变大,安全扫描会变吵,升级风险也更高。

现代写法可以放进pyproject.toml

[project] name = "team-api" version = "0.1.0" requires-python = ">=3.12" dependencies = [ "fastapi>=0.110,<1.0", "uvicorn[standard]>=0.29,<1.0", "pydantic>=2,<3", ] [project.optional-dependencies] postgres = [ "psycopg[binary]>=3.2,<4", ] redis = [ "redis>=5,<6", ] [dependency-groups] dev = [ "pytest>=8,<9", "coverage[toml]>=7,<8", "ruff>=0.8,<1", "mypy>=1.13,<2", ] docs = [ "mkdocs>=1.6,<2", ]

这里有两个容易混淆的概念:

[project.optional-dependencies]是面向用户的 extras。
例如你的库支持 PostgreSQL,用户可以这样安装:

pipinstall"team-api[postgres]"

[dependency-groups]是面向项目内部的开发分组。
例如测试、lint、文档构建、类型检查。Python Packaging 规范说明,依赖组适合 linting、testing 等内部开发用途,也适合不构建分发包的脚本集合;这些依赖组不会作为构建后包的元数据发布。(Python 打包用户指南)

判断标准很简单:

用户是否需要通过 pip install your-package[xxx] 使用它? 是:optional-dependencies 否:dependency-groups

四、一套可直接落地的团队工作流

推荐目录结构:

team-api/ ├── pyproject.toml ├── requirements/ │ ├── prod.txt │ ├── dev.txt │ └── docs.txt ├── src/ ├── tests/ └── .github/workflows/ci.yml

1. 固定 Python 版本

python--version# Python 3.12.x

可以在仓库里加:

# .python-version 3.12

或在 Dockerfile / CI 中明确指定:

strategy:matrix:python-version:["3.12"]

2. 生成生产锁定文件

使用pip-tools

pip-compile\--generate-hashes\-orequirements/prod.txt\pyproject.toml

3. 生成开发锁定文件

如果使用分层 requirements,可以让 dev 受 prod 约束:

# requirements/dev.in -c prod.txt pytest>=8,<9 coverage[toml]>=7,<8 ruff>=0.8,<1 mypy>=1.13,<2

然后:

cdrequirements pip-compile --generate-hashes-odev.txt dev.in

pip-tools文档也给出了 layered requirements 的思路:开发依赖可以通过-c requirements.txt受生产依赖约束,从而保证 dev 和 prod 中共享依赖版本一致。(pip-tools.readthedocs.io)

4. 新同事本地初始化

python-mvenv .venvsource.venv/bin/activate python-mpipinstall-Upip pip-tools pip-sync requirements/prod.txt requirements/dev.txt

Windows PowerShell:

py-3.12-m venv.venv.venv\Scripts\Activate.ps1 python-m pip install-U pip pip-tools pip-sync requirements/prod.txt requirements/dev.txt

5. CI 中禁止“漂移安装”

name:cion:pull_request:push:branches:[main]jobs:test:runs-on:ubuntu-lateststeps:-uses:actions/checkout@v4-uses:actions/setup-python@v5with:python-version:"3.12"-run:python-m pip install-U pip pip-tools-run:pip-sync requirements/prod.txt requirements/dev.txt-run:ruff check .-run:pytest-q

CI 的关键不是“装上最新依赖”,而是“装出和团队约定一致的环境”。


五、为什么“可复现”这么难?

1. 传递依赖会变化

你只写了:

fastapi

但实际会装:

fastapi ├── starlette ├── pydantic ├── typing-extensions └── anyio

任何一个子依赖发布新版本,都可能影响最终环境。

2. 平台不同,wheel 不同

同一个包在 macOS、Linux、Windows 上可能安装不同 wheel。带 C 扩展的包,如numpycryptographypsycopg,更容易受到平台、ABI、系统库影响。

3. Python 版本不同,依赖树不同

很多依赖会写环境标记:

importlib-metadata; python_version < "3.10"

Python 3.9 和 3.12 解析出来的依赖树可能不同。

4.pip install不等于同步

假设环境里已经有:

old-debug-tool==1.0

你运行:

pipinstall-rrequirements.txt

它不会主动删除这个多余包。于是测试可能偷偷依赖了一个锁定文件里没有的包。

5. 未锁哈希,供应链风险更高

只锁版本不锁文件哈希,仍然不如锁版本 + 锁哈希可靠。实际生产项目建议尽量开启 hash checking。


六、常见错误与修复方式

错误 1:直接手写巨大requirements.txt

不推荐:

fastapi==0.115.12 starlette==0.46.2 anyio==4.9.0 sniffio==1.3.1 ...

更推荐:

# requirements.in fastapi>=0.110,<1.0

然后工具生成完整锁定结果。

错误 2:生产和开发混在一起

不推荐:

fastapi pytest ruff mkdocs ipython debugpy

推荐拆开:

requirements/prod.txt requirements/dev.txt requirements/docs.txt

错误 3:库项目过度锁死依赖

如果你写的是库,不应该轻易这样:

dependencies = [ "requests==2.32.3", ]

库应该给兼容范围:

dependencies = [ "requests>=2.30,<3", ]

因为库会被别人的应用集成。真正需要锁死的是“最终应用”的运行环境,而不是所有可复用库的公开依赖。

错误 4:升级依赖没有流程

推荐设定固定升级节奏:

pip-compile--upgrade-orequirements/prod.txt pyproject.toml pip-compile--upgrade-orequirements/dev.txt requirements/dev.in pip-sync requirements/prod.txt requirements/dev.txt pytest

如果只升级某个包:

pip-compile --upgrade-package fastapi-orequirements/prod.txt pyproject.toml

pip-compile默认会尽量保留现有锁定结果;需要升级全部依赖时使用--upgrade,升级单个包时使用--upgrade-package。(pip-tools.readthedocs.io)


七、给团队的一份依赖管理约定

可以直接放进团队 README:

## Dependency Policy 1. 不直接手改生成后的 requirements/*.txt。 2. 新增运行时依赖,修改 pyproject.toml 的 [project].dependencies。 3. 新增测试、lint、docs 工具,修改 [dependency-groups] 或 requirements/*.in。 4. 变更依赖后必须重新生成锁定文件。 5. 本地和 CI 必须使用 sync,而不是随意 pip install。 6. 生产环境只安装 prod 锁定文件。 7. 每次依赖升级必须跑完整测试。 8. Python 小版本、基础镜像、系统包也属于环境契约。

八、实践结论

requirements.txt不是过时文件,但它不应该承担所有角色。它适合作为 pip 安装清单,也可以作为由工具生成的锁定结果;但项目的“意图”更适合写在pyproject.tomlrequirements.in中。

锁定文件解决的是可复现问题。它把“当前解析器碰巧选了什么”固化下来,让新同事、CI、测试环境和生产环境尽可能一致。

依赖分组解决的是环境边界问题。生产只需要运行依赖,开发才需要测试、lint、类型检查、文档和调试工具。

最终建议可以浓缩成一句 Python 最佳实践:

声明依赖要清晰,锁定结果要提交,安装环境要同步,依赖升级要审查。

当团队从“我这里能跑”走向“任何人都能复现”,Python 编程的体验会发生质变:问题更少,发布更稳,协作更轻,代码也更值得信任。

http://www.jsqmd.com/news/803860/

相关文章:

  • CSV的使用方法介绍
  • 团队协作标注避坑指南:如何用Docker和Conda管理Labelme环境,杜绝‘lineColor’报错
  • 从零到一:基于C#与ArcGIS二次开发构建迎风面指数计算插件实战
  • 人生问题变得 可测量的庖丁解牛
  • SM4国密算法实战:从原理到Java代码实现
  • 深入解析 xhs 项目架构:小红书爬虫工具的核心组件与设计模式揭秘
  • Spring Cloud安全架构:JWT认证与授权完整实现指南
  • 2026年最火技术Agent开发!小白程序员如何快速转型拿高薪?赶紧收藏学习!
  • Unity Mod Manager终极指南:5分钟学会游戏模组一键管理
  • OpenAI前首席科学家出庭,揭露CEO奥尔特曼“一贯撒谎模式”
  • 如何在浏览器中解锁微信网页版:完整使用指南与实现原理
  • GenPark主题引擎:配置驱动静态站点样式定制与设计系统实践
  • 小红书爬虫神器xhs:10分钟快速掌握数据获取完整指南
  • 【独家首发】ElevenLabs中文语音优化白皮书:针对普通话声调、儿化音与连读现象的5层微调协议
  • “我想创造”、“我想连接”、“我想理解”、“我想自由”的庖丁解牛
  • Radon与其他工具集成:Flake8、Code Climate、Codacy的完整指南
  • 当1000A牵引电流遇上微安级信号:高铁轨道电路中扼流变压器的‘抗干扰’实战解析
  • 【裂缝识别】检测水下结构中的裂缝及其长度【含Matlab源码 15437期】
  • 合肥豪杰汽车服务:口碑好的合肥商务租车活动租车哪家好 - LYL仔仔
  • 如何快速上手 async-retry:5分钟学会异步重试的完整指南
  • JPlag代码抄袭检测技术方案:多语言源代码相似性分析与聚类系统
  • React Native Actions Sheet与原生性能优化:零依赖的架构设计原理
  • 2025届毕业生推荐的十大AI辅助论文平台实测分析
  • APK Installer:在Windows上智能安装Android应用的终极解决方案
  • 5分钟打造Windows桌面智能监控中心:TrafficMonitor插件生态完全指南
  • Emacs集成AI对话:无缝工作流与高效开发实践
  • __builtin_ffs 在嵌入式实时系统中的高效优先级调度实践
  • 2026年5月河北轻集料混凝土/轻骨料混凝土/轻质混凝土/LC7.5轻集料混凝土/LC5.0轻集料混凝厂家解析,认准廊坊畅销环保科技有限公司 - 2026年企业推荐榜
  • Go-sniffer 安全指南:如何安全使用网络嗅探工具进行调试
  • Conda环境yml文件配置:集成PIP与国内镜像源的实战指南