1. 课程导入与工程全景
很多 Python 项目在开发者本机运行良好,一旦交付给同事、部署到服务器或发布到 PyPI,就会立刻暴露出一连串问题:ModuleNotFoundError、版本冲突、编译失败、环境不一致、安装后缺少数据文件。这些现象背后并不是 Python 语言本身的问题,而是打包与依赖管理这一工程环节没有被系统处理。本课程要解决的,正是从"能跑"到"可交付、可复现、可发布"之间的这段工程距离。
一、两个核心问题:打包与依赖
先明确两个概念。所谓打包(packaging),是把源码、包结构、元数据、资源文件、入口脚本组织成一个可被安装、可被分发的构件,常见形态是 wheel(.whl)和源码分发包(.tar.gz)。所谓依赖管理(dependency management),是声明项目需要哪些第三方库、允许什么版本范围,并把这些约束解析成一组确定、可复现的安装集合。
两者是耦合的:打包产物的元数据里必须写清依赖声明,否则安装者拿到的 wheel 无法自动拉取正确依赖;而依赖解析的结果又必须能被锁定和复现,否则今天装得上、明天装不上。理解这一点,就能理解为什么本课程不把二者拆开讲。
二、典型痛点:从现场故障反推工程缺口
下面这些场景在真实项目中反复出现,它们分别指向不同的工程缺口。
- 依赖漂移:
requirements.txt里只写requests,没有版本约束。今天解析到 2.31,半年后解析到 2.32,行为差异导致线上报错。缺口在于缺少锁定文件与版本策略。 - 环境不一致:开发用 Python 3.11,服务器是 3.8,代码里用了
match语句或新类型语法,部署即失败。缺口在于缺少requires-python声明与 CI 多版本验证。 - 隐式依赖:本机恰好装过
numpy,代码直接 import,但项目元数据里没写。换台干净机器就崩。缺口在于依赖声明不完整。 - 包结构错误:源码放在
src/下但没配package-dir,或者缺少__init__.py,导致pip install .后 import 不到子包。缺口在于构建后端配置。 - 资源文件丢失:模板、配置、模型权重放在包目录里,但没在打包配置中声明,wheel 里根本没有这些文件。缺口在于数据文件包含规则。
- 依赖冲突:A 库要求
urllib3<2,B 库要求urllib3>=2,解析器报ResolutionImpossible。缺口在于缺少冲突定位与版本协商能力。
把这些痛点归位,可以看到它们分别落在项目结构、元数据声明、依赖解析、构建产物、发布分发、环境复现六个环节上。本课程的学习路径正是沿这六个环节展开。
三、工具生态全景
Python 打包生态经历过多次演进,今天的主流格局可以按职责分层理解。
| 层次 | 代表工具 | 职责 |
|---|---|---|
| 构建后端 | setuptools、hatchling、flit、pdm-backend | 把源码构建成 wheel/sdist,读取 pyproject.toml |
| 构建前端 | build、pip wheel | 调用后端,产出构件 |
| 依赖与环境 | pip + venv、pip-tools、Poetry、PDM、uv、Conda | 解析、锁定、安装依赖,管理虚拟环境 |
| 发布分发 | twine、PyPI、私有索引(devpi、Artifactory) | 上传构件、托管索引 |
| 质量与验证 | pytest、tox、nox、GitHub Actions | 多环境测试、CI 自动构建发布 |
需要强调的是:这些工具不是互斥的替代关系,而是可以组合的。一个常见组合是 pyproject.toml + hatchling 负责构建,uv 或 pip-tools 负责锁定,twine 负责上传,GitHub Actions 负责串联。课程后续会逐一展开,但先建立这张全景图,避免陷入"工具选型焦虑"。
四、一张图看清工程链路
下图把从源码到可安装构件的完整链路、每段链路的输入输出、以及对应的工具与常见故障点放在一起,作为本课程的总览地图。
五、学习路径与本节定位
基于上面的链路,本课程的学习路径按"先能装、再能锁、后能发"推进:
- 项目结构与元数据:理解
pyproject.toml的字段含义,掌握 src 布局与包发现规则。 - 依赖声明与锁定:区分直接依赖与传递依赖,掌握版本约束语法与 lock 文件的作用。
- 构建与产物检查:用
python -m build产出 wheel,解压检查内容是否完整。 - 发布与私有索引:用
twine上传,理解 PyPI 与私有源的差异。 - 环境复现与 CI:在干净环境验证安装,用 CI 固化流程。