在昨天的文章【Python】通过Editable Install
模式详解,解决Python开发总是import出错的问题 中, 我们提到了Python项目的配置文件requirements.txt
、setup.py
、pyproject.toml
。在昨天的解决方案中, 我们同时维护了这三个文件。 由同学就问:真的需要这么复杂的同时维护三个文件吗? 今天,田辛老师就来详细解释一下这三个文件。 包括他们的核心用途、演变历史等等。 希望通过这篇文章, 读者思考在你的项目中, 是否需要保留他们。
1. requirements.txt
: 最最简单的依赖列表
定位:最基础的依赖声明文件,通常用于直接安装依赖。
用途:
- 列出项目运行所需的第三方包及其版本(例如
requests==2.25.1
)。 - 通过
pip install -r requirements.txt
一键安装依赖。 - 常用于开发环境或生成依赖快照(如
pip freeze
)。
局限性:
- 仅记录依赖包,不区分开发依赖、生产依赖或操作系统级的依赖。
- 无法定义包的安装方式(例如从Git仓库或本地路径安装)。
- 与项目打包(如生成
.whl
或上传到PyPI
)无关。
2. setup.py
:传统的打包配置
2.1. 定位:
基于 setuptools
的打包工具配置文件,用于构建和分发Python
包。
2.2. 用途:
- 定义项目元数据(名称、版本、作者等)。
- 声明依赖(
install_requires
)、开发依赖(extras_require
)以及可执行脚本。 - 通过
python setup.py install
或pip install .
安装本地包。
2.3. 示例片段:
from setuptools import setupsetup(name="tdouya_tools",version="0.1",install_requires=["requests>=2.25"],extras_require={"dev": ["pytest"]},
)
2.4. 缺点:
- 配置复杂,需要编写
Python
代码(而非声明式配置)。 - 依赖动态执行(可能存在安全隐患)。
- 无法直接支持现代构建后端(如
poetry
或flit
)。
3. pyproject.toml
:现代的统一配置
3.1. 定位:
PEP 518
引入的标准化配置文件,旨在统一项目构建和元数据配置。
3.2. 用途:
- 指定构建系统的依赖(如 setuptools、poetry 或 flit)。
- 声明项目元数据、依赖及版本约束(遵循PEP 621标准)。
- 支持动态依赖分离(生产依赖、开发依赖、测试依赖等)。
3.3. 示例片段
使用 PEP 621 格式
[project]
name = "my_project"
version = "0.1"
dependencies = ["requests>=2.25"][project.optional-dependencies]
dev = ["pytest"]
优势:
- 声明式配置:更简洁且易于维护。
- 兼容性:支持现代工具链(如
poetry
、pipenv
)。 - 扩展性:可整合代码格式化、测试配置等其他工具(如
black
、coverage
)。
4. 三者是否需要共存?
田老师回答: 通常不需要,但取决于项目需求和工具链。对于某些场景,同时保留 requirements.txt 和 setup.py 可能带来实际价值。
4.1. 场景分析
4.1.1. 传统项目(仅用 setuptools
)
- 需要
setup.py
+requirements.txt
:setup.py
用于定义打包元数据和依赖(install_requires
),适合发布到PyPI
。requirements.txt
可以细化开发环境依赖(如测试框架pytest
、代码格式化工具black
),并通过pip install -r requirements.txt
快速安装。- 优势:职责分离,
setup.py
关注生产依赖和分发,requirements.txt
管理开发工具和环境复现。
这也是田辛老师的项目中, 往往这两个文件同时存在的原因。
4.1.2. 兼容旧工具链或脚本
- 许多CI/CD流水线、自动化脚本或开发者习惯依赖 requirements.txt 快速安装依赖。
- 保留 setup.py 可以兼容 python setup.py install 等传统构建命令,避免破坏遗留工作流程。
- 示例:
# 开发时安装开发依赖 pip install -r requirements.txt # 构建时通过 setup.py 安装生产包 python setup.py build
4.1.3. 渐进式迁移到 pyproject.toml
在过渡阶段,同时保留 setup.py
和 requirements.txt
可降低迁移风险:
pyproject.toml
定义核心配置和构建后端。setup.py
作为备用接口(例如兼容未完全支持PEP 621
的工具)。requirements.txt
继续用于开发环境依赖安装。
4.1.4. 依赖管理的灵活性
4.1.4.1. 细粒度控制:
- 在 requirements.txt 中指定复杂依赖(如 Git 仓库、本地路径或特定系统级包)。
- 在 setup.py 的 install_requires 中仅声明最小化生产依赖,保持分发包的轻量性。
4.1.4.2. 示例:
# setup.py 中仅保留核心依赖
install_requires = ["requests>=2.25"]
# requirements.txt 包含开发工具和环境约束
pytest==7.4.0
black==23.9.1
-e . # 以可编辑模式安装当前包
4.1.4.3. 关键决策点
- 是否依赖旧工具或脚本:若团队或工具链重度依赖
requirements.txt
,保留它可以减少迁移成本。 - 是否需要快速环境复现:
requirements.txt
结合pip freeze
能生成精确的依赖快照,适合快速复制开发环境。 - 是否需要兼容非打包场景:例如在
Dockerfile
中直接使用requirements.txt
安装依赖,而非通过包构建流程。
5. 最佳实践建议
5.1. 新项目优先使用 pyproject.toml
通过 PEP 621
或工具(如 poetry
)定义元数据和依赖,无需 setup.py
或 requirements.txt
。
5. 2. 旧项目逐步迁移
保留 setup.py
但逐步将配置转移到 pyproject.toml
,例如使用 setuptools
的 [project] 表。
5.3 . 区分 requirements.txt
的使用场景
如果仍需用它,可以自动生成:
# 通过 poetry 生成
poetry export -f requirements.txt --output requirements.txt
5.4 . 避免重复声明依赖
不要同时在 setup.py
和 pyproject.toml
中声明依赖,否则会导致维护负担。
6. 结论
在现代1Python1生态中,pyproject.toml
是未来的方向,能够取代 setup.py
和 requirements.txt
的大部分功能。对于新项目,推荐仅使用 pyproject.toml
并结合工具(如 poetry
)管理依赖和构建流程。旧项目可视情况逐步迁移,无需强制三者共存。最终目标是简化配置、减少冗余,并拥抱标准化。