pip高级用法与依赖管理
掌握Python包管理的进阶技巧
一、前言与概述
Python的包管理生态中,pip 是最基础也是最核心的工具。绝大多数开发者仅使用 pip install package 和 pip install -r requirements.txt 两条命令,但这远远没有发挥 pip 的全部能力。在真实项目中——尤其是多人协作、多环境部署、私有包管理场景下——深入了解 pip 的高级特性是区分"会用"和"精通"的关键分水岭。
本文将覆盖以下核心主题:requirements.txt 的高级格式语法(版本约束、环境标记、VCS引用)、pip freeze 与 pip list 的差异与适用场景、pip install 关键选项(--no-deps、--no-build-isolation、--find-links、--extra-index-url)、依赖锁定与 pip-tools、私有包索引与镜像源配置、pipx 隔离安装、Wheel 与源码分发的区别以及 pip 缓存管理。
学习目标
- 掌握 requirements.txt 的全部格式特性,能编写生产级依赖文件
- 理解 pip 依赖解析机制,能诊断和解决依赖冲突
- 学会使用 pip-tools 实现可复现的依赖锁定
- 掌握私有包索引与多源配置,适应企业级开发环境
- 理解 Wheel 与源码分发的区别,优化包安装性能
二、requirements.txt 高级格式
大多数开发者将 requirements.txt 视为简单的"包名+版本号"列表,但实际上 pip 支持的 requirements 文件格式远比这更丰富。
2.1 基础格式回顾
每一行定义一个依赖项,支持注释(以 # 开头)和空行。
基础 requirements.txt
# 这是注释
flask
# 指定版本
requests==2.31.0
# 版本范围
numpy>=1.21.0, <2.0.0
# 排除版本
pandas!=1.4.0
2.2 版本约束符号详解
| 符号 | 含义 | 示例 | 说明 |
== | 精确匹配 | django==4.2.0 | 只安装该精确版本 |
>= | 大于等于 | python-dateutil>=2.8 | 最低版本要求 |
> | 大于 | click>8.0 | 排除该版本及之前版本 |
< | 小于 | urllib3<2.0 | 上限约束(不常用) |
<= | 小于等于 | pyyaml<=6.0 | 上限约束 |
!= | 排除版本 | cryptography!=38.0.0 | 跳过已知有问题的版本 |
~= | 兼容版本 | pydantic~=2.0 | 等价于 >=2.0, ==2.* |
~= 操作符是兼容版本约束(Compatible Release),其语义为"不低于指定版本且不超出下一个有序的不兼容版本"。例如 pydantic~=2.4.0 等价于 >=2.4.0, ==2.4.*,即允许 2.4.x 范围内的小版本升级,但不允许升级到 2.5.0 或 3.0.0。
2.3 环境标记(Environment Markers)
最强大但最容易被忽略的特性:可以根据目标平台、Python 版本等因素条件性地安装依赖。
使用环境标记的 requirements.txt
# 仅 Python 3.10+ 需要
dataclasses; python_version >= "3.10"
# 仅 Windows 需要
pywin32; sys_platform == "win32"
# 仅 macOS 需要
pyobjc-framework-Cocoa; sys_platform == "darwin"
# 仅 Linux 需要
python-prctl; sys_platform == "linux"
# 仅 CPython 实现需要
cryptography; implementation_name == "cpython"
# Python 3.9 以下使用 importlib_metadata 作为 backport
importlib-metadata; python_version < "3.9"
# 64 位架构需要
tensorflow; platform_machine == "x86_64"
# 支持多个条件的复杂组合
pyside6; sys_platform == "win32" or sys_platform == "darwin"
numpy>=1.21; platform_python_implementation != "PyPy"
支持的环境变量完整列表如下:
| 标记名称 | 描述 | 示例值 |
os_name | 操作系统名称 | nt, posix |
sys_platform | 系统平台 | win32, linux, darwin |
platform_machine | 机器架构 | x86_64, aarch64 |
platform_python_implementation | Python 实现 | CPython, PyPy |
platform_system | 系统名称 | Windows, Linux, Darwin |
python_version | Python 版本 | 3.10, 3.11 |
python_full_version | 完整 Python 版本 | 3.10.12 |
implementation_name | 实现名称 | cpython, pypy |
implementation_version | 实现版本 | 3.10.12 |
extra | 额外特性名称 | test, dev |
最佳实践:生产级 requirements.txt 示例
# requirements.txt - 生产环境
# 核心框架
fastapi>=0.100.0, <0.110.0
uvicorn[standard]>=0.23.0
# 数据库
sqlalchemy>=2.0, <2.1
asyncpg; sys_platform != "win32" # Windows 上没有 asyncpg
aiosqlite; sys_platform == "win32"
# 仅在 PyPy 下使用纯 Python 替代品
psycopg2-binary; platform_python_implementation == "CPython"
psycopg2cffi; platform_python_implementation == "PyPy"
# 异步支持 — 仅在 Python 3.11+ 不需要 backport
exceptiongroup; python_version < "3.11"
# 仅生产环境
gunicorn; sys_platform != "win32"
2.4 直接引用(Direct References)
使用 @ 语法可以直接指向一个具体的发行包 URL、本地路径或者 Wheel 文件,跳过 PyPI 查找过程。
直接引用格式
# 直接从 URL 安装
mypackage @ https://example.com/packages/mypackage-1.0.0-py3-none-any.whl
# 从本地路径安装
mypackage @ file:///home/user/packages/mypackage-1.0.0.tar.gz
# 使用相对路径(推荐在项目仓库内使用)
mypackage @ ./vendor/mypackage-1.0.0-py3-none-any.whl
# 从本地开发目录安装(可编辑模式)
-e ./mypackage
2.5 VCS 引用(Version Control System References)
直接从 Git、Mercurial、Subversion 等版本控制系统安装包,对于依赖尚未发布的修复或私有仓库中的代码极为实用。
VCS 引用格式
# 从 Git 仓库安装(默认分支)
git+https://github.com/author/mypackage.git
# 指定分支
git+https://github.com/author/mypackage.git@main
# 指定标签
git+https://github.com/author/mypackage.git@v1.2.3
# 指定 commit hash
git+https://github.com/author/mypackage.git@abc123def456
# SSH 方式(需要配置 SSH 密钥)
git+ssh://git@github.com/author/private-repo.git@main
# 带子目录(当包位于仓库子目录中时)
git+https://github.com/author/monorepo.git@main#subdirectory=packages/foo
# 同时支持 -e 可编辑模式
-e git+https://github.com/author/mypackage.git@main
注意事项
- VCS 引用在
pip freeze 输出中会保留完整 URL,可能会泄露私有仓库地址
- CI/CD 环境中使用 SSH 引用需要妥善处理部署密钥
- monorepo 场景下
subdirectory 参数非常有用,但要求 pip 版本 >= 21.3
三、pip freeze 与 pip list 深度对比
这两个命令常被混淆,但它们的用途完全不同。
| 特性 | pip freeze | pip list |
| 输出格式 | package==version(可直接写入 requirements.txt) | 表格形式(包名 + 版本) |
| 默认输出范围 | 仅显示通过 pip 安装的包 | 显示所有已安装包(含 setuptools、pip 自身) |
| 常用选项 | --exclude(排除特定包)、--local | --outdated、--uptodate、--format |
| 主要用途 | 生成 requirements.txt | 查看环境状态、检查更新 |
常用命令示例
# 生成当前环境的依赖列表
pip freeze > requirements.txt
# 排除特定包(如不需要的测试框架)
pip freeze --exclude pytest --exclude pytest-cov > requirements.txt
# 只输出通过 pip 安装的包(排除 setuptools、pip 等)
pip freeze --local > requirements.txt
pip list 命令
# 列出所有包
pip list
# 仅列出有过期版本的包
pip list --outdated
# 以 JSON 格式输出(方便程序解析)
pip list --format=json
pip list --outdated --format=columns
# 列出最新版本的包
pip list --uptodate
重要区别
pip freeze 默认会输出所有依赖的传递依赖(transitive dependencies),而 pip list 展示环境中所有已安装的包。因此 pip freeze > requirements.txt 直接用于锁定文件是最方便的——但这也意味着它会包含许多你并未直接声明的包。
四、pip install 高级选项
以下选项在日常开发中容易被忽略,但在解决复杂依赖问题时不可或缺。
4.1 --no-deps:仅安装目标包,不安装依赖
当你只需要一个包的元数据(如 type stubs)或者依赖已通过其他方式满足时使用。
pip install mypy --no-deps
4.2 --no-build-isolation:禁用构建隔离
默认情况下,pip 在构建 wheel 时会在一个隔离环境中安装构建依赖。禁用后使用当前环境的构建依赖,这对需要系统级依赖(如 libpq-dev)的包很有用,也可以加速 CI 中的重复构建。
pip install psycopg2 --no-build-isolation
注意事项
--no-build-isolation 需要你已自行安装好所有构建依赖(如 setuptools、wheel、cython 等),否则构建可能失败。
4.3 --find-links:指定备选查找路径
可以指向本地目录或HTML 页面(包含包下载链接),pip 会从中搜索包文件。
# 指向本地 Wheel 仓库
pip install --find-links ./wheelhouse mypackage
# 指向私有索引页
pip install --find-links https://privateserver.com/packages/ mypackage
# 也可以与 --index-url 组合使用
pip install \
--index-url https://pypi.org/simple/ \
--find-links https://private-packages.example.com/wheels/ \
mypackage
--find-links vs --index-url
--index-url:指定 PyPI 兼容的包索引(必须有 /simple 格式的 API)
--find-links:可以指向任何包含 Wheel/源码包的 URL 或本地目录
- 同时使用时,pip 会先搜索
--find-links,再搜索 --index-url
4.4 --extra-index-url:添加额外包索引
当公司有私有 PyPI 服务器时,保留默认 PyPI 的同时添加私有源是非常常见的场景。
pip install mypackage \
--index-url https://pypi.org/simple/ \
--extra-index-url https://private-pypi.company.com/simple/
安全警告
--extra-index-url 会同时搜索多个源。如果私有源中找不到包,pip 会降级到公有 PyPI。攻击者可以通过在公有 PyPI 上注册一个同名依赖包来进行依赖混淆攻击(Dependency Confusion)。
防御方法:使用 --index-url 单独指向私有源,结合 --extra-index-url 或 --find-links 来精确控制包的来源。
4.5 其他实用选项
# 限制并发下载数(默认 16)
pip install --max-concurrent-downloads 8
# 仅使用二进制包(wheel),跳过源码构建
pip install --only-binary=:all: numpy
# 强制从源码构建(不使用 wheel)
pip install --no-binary numpy
# 指定约束文件—允许限制依赖版本但不直接安装
pip install -c constraints.txt flask
# 静默模式(不输出进度条,在 CI 中很有用)
pip install -q package
# 重试设置
pip install --retries 5 --timeout 30 package
约束文件 vs requirements 文件
约束文件(constraints file,通过 -c 参数指定)只限制版本,不引入新的包。这是与 requirements 文件的本质区别。常用于统一团队中多个项目的依赖版本范围。
# constraints.txt
numpy>=1.22, <1.25
pandas>=1.4, <1.6
requests==2.31.0
# 使用约束文件
pip install -c constraints.txt flask fastapi
五、pip-tools 与依赖锁定
pip-tools 是 pip 生态中最流行的依赖锁定工具,受到 npm 的 package-lock.json 启发,实现了 requirements.in + requirements.txt 双文件工作流。
5.1 安装 pip-tools
pip install pip-tools
5.2 基本工作流
步骤一:编写 requirements.in,只列出顶层依赖(top-level dependencies)。
requirements.in
# 只声明直接依赖
fastapi
uvicorn[standard]
sqlalchemy
psycopg2-binary
alembic
redis
步骤二:使用 pip-compile 生成锁定后的完整依赖文件。
# 生成 requirements.txt(包含所有传递依赖的精确版本)
pip-compile requirements.in
# 指定输出文件
pip-compile requirements.in --output-file requirements.txt
# 升级所有依赖到最新兼容版本
pip-compile --upgrade requirements.in
# 只升级某个特定包
pip-compile --upgrade-package flask requirements.in
# 生成带注释的输出(标注每个包来自哪个顶层依赖)
pip-compile --annotate requirements.in
步骤三:使用 pip-sync 将当前环境与锁定文件精确同步。
# 同步环境(会卸载锁定文件中不存在的包!)
pip-sync requirements.txt
# 如果还在开发中,保留 pip 和 pip-tools
pip-sync requirements.txt --pip-args "--no-deps"
5.3 pip-compile 输出示例
生成的 requirements.txt
#
# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
# pip-compile requirements.in
#
alembic==1.12.1
# via -r requirements.in
anyio==4.1.0
# via starlette
click==8.1.7
# via
# uvicorn
# alembic
fastapi==0.108.0
# via -r requirements.in
greenlet==3.0.1
# via sqlalchemy
h11==0.14.0
# via uvicorn
idna==3.6
# via anyio
mako==1.3.0
# via alembic
markupsafe==2.1.3
# via mako
psycopg2-binary==2.9.9
# via -r requirements.in
redis==5.0.1
# via -r requirements.in
sniffio==1.3.0
# via anyio
sqlalchemy==2.0.25
# via
# -r requirements.in
# alembic
starlette==0.32.0.post1
# via fastapi
typing-extensions==4.9.0
# via
# fastapi
# sqlalchemy
# alembic
uvicorn[standard]==0.25.0
# via -r requirements.in
pip-tools 的核心价值
- 可复现性:锁定所有传递依赖的精确版本,确保不同环境完全一致
- 可追溯性:注释标明每个包的来源,依赖关系一目了然
- 安全更新:通过
--upgrade-package 精准控制升级范围
- 分环境管理:可以为 dev、test、prod 分别维护不同的
.in 文件
5.4 多环境管理
requirements/dev.in
# 开发环境依赖
-c common.txt # 引用约束文件
-r base.in # 引用基础依赖
pytest>=7.0
pytest-cov
pytest-asyncio
black
ruff
mypy
pre-commit
生成开发环境锁定文件
pip-compile requirements/dev.in --output-file requirements/dev.txt
六、依赖解析与冲突解决
6.1 pip 的依赖解析机制
从 pip 20.3(2020年底)开始,新版的依赖解析器(dependency resolver)被设为默认。其核心行为是回溯式解析(backtracking):当发现版本冲突时,会回溯并尝试其他版本组合,直到找到一组满足所有约束的版本——或者确认无解。
依赖解决过程示例
# 假设你运行:
pip install "fastapi>=0.100" "sqlalchemy<1.4"
# pip 的解析过程:
# 1. 选中 fastapi 最新版(如 0.108.0)
# 2. 发现 fastapi 依赖 pydantic>=1.7.4
# 3. 继续查看 sqlalchemy<1.4 的依赖
# 4. 检查所有这些依赖之间是否有版本冲突
# 5. 如果没有冲突,锁定所有版本
# 6. 如果有冲突,回溯尝试 fastapi 的旧版本
# 7. 重复直到找到可行组合或报告错误
6.2 常见冲突场景与解决方案
场景一:传递依赖冲突
# ERROR: Cannot install package-a==1.0 and package-b==2.0
# because these package versions have conflicting dependencies.
# package-a 1.0 depends on shared-lib>=2.0
# package-b 2.0 depends on shared-lib<2.0
# 解决方案:
# 1. 升级/降级 package-a 或 package-b 到兼容版本
# 2. 在约束文件中固定 shared-lib 的版本
pip install "package-a==1.1" "package-b==2.0"
场景二:使用约束文件缓解冲突
# constraints.txt
shared-lib>=1.9, <2.1
pip install -c constraints.txt package-a package-b
场景三:查看冲突详情
# 使用 --verbose 获取详细的依赖解析过程
pip install package-a package-b --verbose
# 使用 pipdeptree 可视化依赖树
pip install pipdeptree
pipdeptree -p package-a
诊断依赖问题的工具链
- pipdeptree:显示依赖树,快速定位冲突
- pip check:验证当前环境中所有已安装包的依赖关系是否一致
- pip install --dry-run:只解析依赖但不实际安装(pip 23.1+)
- docker 镜像中的 pip list --outdated:定期检查过期的依赖
pipdeptree 输出示例
$ pipdeptree -p fastapi
fastapi==0.108.0
- pydantic [required: >=1.7.4, !=1.8, !=1.8.1, !=2.0.0, !=2.0.1, !=2.1.0, installed: 2.5.2]
- typing-extensions [required: >=4.6.1, installed: 4.9.0]
- starlette [required: >=0.29.0, !=0.33.0, installed: 0.32.0.post1]
- anyio [required: >=3.4.0, <5, installed: 4.1.0]
- idna [required: >=2.8, installed: 3.6]
- sniffio [required: >=1.1, installed: 1.3.0]
- typing-extensions [required: >=4.8.0, installed: 4.9.0]
七、导出 requirements.txt 的最佳实践
团队协作中,一个精心维护的 requirements.txt 比口头约定的"大家都装什么版本"要可靠得多。以下是最佳实践建议。
推荐的多文件策略
# 项目依赖文件布局
requirements/
base.in # 基础依赖(生产共用)
base.txt # 基础锁定文件(由 pip-compile 生成)
dev.in # 开发依赖
dev.txt # 开发锁定文件
test.in # 测试依赖
test.txt # 测试锁定文件
ci.in # CI 依赖
ci.txt # CI 锁定文件
requirements.txt # 指向 base.txt 的符号链接或软引用
7.1 精确控制 vs 宽松约束
| 场景 | 推荐方式 | 原因 |
| 应用项目(Application) | 锁定精确版本 | 保证可复现部署 |
| 库项目(Library) | 使用宽松版本范围 | 兼容更多下游使用者 |
| CI 环境 | 锁定 + 定期自动升级 | 平衡稳定性与安全性 |
| 开发环境 | 锁定 + 手动升级 | 团队成员间保持一致 |
7.2 导出简化技巧
# 仅导出顶层依赖(配合 pip-tools)
pip-compile --no-annotate --no-header requirements.in
# 移除 pip freeze 输出的某些包
pip freeze --exclude pip --exclude setuptools --exclude wheel
# 在 CI 中检查 requirements 是否有变动
pip-compile --check requirements.in
7.3 不要提交 pip freeze 的直接输出
为什么不应直接使用 pip freeze > requirements.txt
- 会包含当前环境的所有包(包括那些通过其他方式安装的)
- 无法区分"直接依赖"和"传递依赖",导致升级困难
- 不同平台上 freeze 结果可能不同(系统包混入)
- 缺少与环境标记、安全更新相关的元信息
推荐做法:使用 pip-compile 生成锁定文件,同时保留 .in 文件用于声明顶层依赖。
八、私有包索引与镜像源配置
8.1 配置镜像源
在国内网络环境下,使用 PyPI 官方源时常遇到超时或速度缓慢的问题。配置镜像源是最直接的解决方案。
临时使用镜像源
pip install numpy -i https://pypi.tuna.tsinghua.edu.cn/simple
全局配置镜像源
# Linux/macOS: ~/.config/pip/pip.conf
# Windows: %APPDATA%\pip\pip.ini
[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
trusted-host = pypi.tuna.tsinghua.edu.cn
# 也可以使用阿里云镜像
# index-url = https://mirrors.aliyun.com/pypi/simple/
# 或使用华为云镜像
# index-url = https://repo.huaweicloud.com/repository/pypi/simple/
在项目级别配置 pip.conf
# 项目根目录下放置 pip.conf
[global]
index-url = https://private-pypi.company.com/simple/
extra-index-url = https://pypi.tuna.tsinghua.edu.cn/simple/
trusted-host =
private-pypi.company.com
pypi.tuna.tsinghua.edu.cn
8.2 使用环境变量配置源
# 在 CI/CD 中使用环境变量覆盖默认源
export PIP_INDEX_URL=https://private-pypi.company.com/simple/
export PIP_EXTRA_INDEX_URL=https://pypi.org/simple/
export PIP_TRUSTED_HOST=private-pypi.company.com
pip install -r requirements.txt
8.3 搭建私有 PyPI 源
企业内部通常需要私有包索引来托管业务相关的 Python 包。以下是几种主流方案。
使用 devpi 搭建
# 安装 devpi-server
pip install devpi-server
devpi-server --host 0.0.0.0 --port 3141
# 初始化 devpi
devpi use http://localhost:3141
devpi login root --password=""
devpi index -c dev bases=root/pypi
# 上传包
devpi upload dist/mypackage-1.0.0-py3-none-any.whl
使用 pypiserver 搭建(更轻量)
# 安装 pypiserver
pip install pypiserver
# 启动服务(指向本地包目录)
pypiserver --port 8080 --passwords=.htpasswd packages/
# 上传 Wheel 文件到 packages/ 目录即可
# 客户端配置
pip install --index-url http://pypi-server:8080/simple/ mypackage
推荐的企业解决方案对比
| 工具 | 特点 | 适用场景 |
| devpi | 功能完整,支持镜像、缓存、测试索引 | 中型团队,需要完整功能 |
| pypiserver | 极简,基于文件系统 | 小型团队或 CI 场景 |
| Artifactory (JFrog) | 企业级,支持多种包格式 | 大型企业,多语言产物管理 |
| GitLab PyPI Registry | 与 GitLab CI 集成 | 已在用 GitLab 的团队 |
| AWS CodeArtifact | 云原生,自动伸缩 | AWS 用户 |
九、pipx:隔离安装 Python 应用
pipx 是一个专门用于安装和运行 Python 终端应用的工具。它会为每个应用创建独立的虚拟环境,避免应用依赖污染全局环境或相互冲突。
安装 pipx
# 推荐方式
pip install pipx
pipx ensurepath
# macOS
brew install pipx
# Linux (apt)
sudo apt install pipx
pipx 常用命令
# 安装应用(自动创建隔离环境)
pipx install poethepoet
pipx install black
pipx install ruff
pipx install cookiecutter
# 临时运行应用而不安装
pipx run pycowsay "Hello, pipx!"
# 列出已安装的应用
pipx list
# 升级应用
pipx upgrade-all
# 卸载
pipx uninstall poethepoet
# 从 GitHub 直接运行
pipx run https://github.com/author/repo.git
# 查看 pipx 管理的所有环境
pipx list --verbose
何时使用 pipx 而不是 pip
- 应该用 pipx:全局安装 CLI 工具(black、mypy、poetry、httpie、jupyter-lab 等)
- 应该用 pip:在项目虚拟环境中安装库依赖
- 注意:pipx 适合应用,不适合库
十、Wheel vs Source Distribution
10.1 核心概念
| 特性 | Wheel(.whl) | Source Distribution(sdist, .tar.gz) |
| 本质 | 预构建的二进制分发格式 | 源码包,需要构建步骤 |
| 安装速度 | 快(本质是解压 + 复制) | 慢(需要解压 + 可能编译) |
| 构建依赖 | 不需要构建工具 | 需要 setuptools、wheel 等 |
| 是否编译 | 已编译的 .so/.pyd 文件 | 需要编译 C 扩展 |
| 平台相关 | 有平台标签(如 win_amd64) | 大部分是纯 Python 或跨平台 |
| Python 版本 | 有 ABI 标签(如 cp311) | 通常不限 Python 版本 |
查看包的 wheel 标签
# numpy 的 wheel 文件命名示例
numpy-1.26.2-cp311-cp311-win_amd64.whl
# ^^^^^ ^^^^^ ^^^^^^^^^^^
# 版本 Python ABI 平台
# 纯 Python 包(跨平台)
requests-2.31.0-py3-none-any.whl
# ^^^ ^^^^ ^^^
# py3 无ABI 跨平台
10.2 创建与分发
构建 Wheel
# 传统方式
pip install wheel
python setup.py bdist_wheel
# 现代方式(推荐)
pip install build
python -m build --wheel
# 构建源码包 + wheel
python -m build
安装时的选择优先级
# pip 默认优先选择 wheel(更快)
# 强制使用 wheel(禁用源码构建)
pip install --only-binary=:all: numpy
# 强制从源码构建
pip install --no-binary numpy
10.3 理解平台兼容性
Manylinux 标准
# manylinux 标签说明兼容的 Linux 发行版范围
numpy-1.26.2-cp311-cp311-manylinux_2_17_x86_64.whl
# ^^^^^^^^^^^^^^^^^^^^^^^^^
# manylinux_2_17 = 兼容 glibc 2.17+
# x86_64 = 64 位 x86 架构
# 查看当前系统的 manylinux 兼容性
pip debug --verbose | findstr manylinux # Windows
pip debug --verbose | grep manylinux # Linux/macOS
关键知识点
- 纯 Python 包(如 requests、click):使用
py3-none-any 的通用 wheel,任何平台和 Python 3 版本都可安装
- 含 C 扩展的包(如 numpy、pandas):需要平台特定 wheel,PyPI 为常见平台提供预编译包
- 无可用 wheel 时:pip 自动回退到 sdist,但需要编译环境(gcc/msvc 等)
- 企业建议:在私有源中同时发布 wheel 和 sdist,优先使用 wheel
十一、pip 缓存管理
pip 会缓存下载的包文件,避免重复下载同名文件。高效管理缓存可以显著加速 CI/CD 构建过程。
11.1 缓存目录位置
# Linux/macOS
~/.cache/pip/
# Windows
%LocalAppData%\pip\Cache\
# 查看当前缓存路径
pip cache info
# 输出示例
# Package index page cache location: ...
# Package cache location: ...
# Wheel cache location: ...
11.2 缓存管理命令
# 查看缓存大小
pip cache info
# 列出所有缓存的 wheel
pip cache list
pip cache list --format=abspath
# 删除所有缓存
pip cache purge
# 删除指定包的缓存
pip cache remove numpy
# 在 CI 中暖缓存(CI 构建前先下载但不安装)
pip download -r requirements.txt --dest /tmp/pip-cache-warm
11.3 CI/CD 中的缓存策略
GitHub Actions 示例
# .github/workflows/ci.yml
- name: Cache pip
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: pip-${{ runner.os }}-${{ hashFiles('requirements/*.txt') }}
restore-keys: |
pip-${{ runner.os }}-
pip-
- name: Install dependencies
run: |
pip install --upgrade pip
pip install -r requirements/dev.txt
GitLab CI 示例
# .gitlab-ci.yml
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .cache/pip
before_script:
- export PIP_CACHE_DIR="$(pwd)/.cache/pip"
- pip install -r requirements.txt
缓存优化建议
- 在 CI 中将缓存 key 与 requirements 文件哈希绑定,只在依赖变更时重建缓存
- 使用
--no-index --find-links 配合预下载的 wheel 目录,实现完全离线的安装流程
- 定期执行
pip cache purge,避免缓存无限制增长
- 对于大型 monorepo,考虑使用
PIP_NO_CACHE_DIR=0 显式启用缓存(默认就是启用的)
十二、完整实战案例
以下是一个真实项目的依赖管理全过程,综合运用本文介绍的各种技术。
场景:从零搭建一个 FastAPI 微服务项目
步骤 1:创建项目结构
myproject/
requirements/
base.in
dev.in
test.in
ci.in
pyproject.toml
src/
tests/
步骤 2:编写顶层依赖
requirements/base.in
# 生产依赖
fastapi>=0.100.0
uvicorn[standard]>=0.23.0
sqlalchemy>=2.0
asyncpg==0.29.0
alembic>=1.12
redis>=5.0
pydantic-settings>=2.0
httpx>=0.25 # HTTP 客户端
python-jose[cryptography]>=3.3 # JWT 认证
步骤 3:生成锁定文件
pip-compile requirements/base.in --output-file requirements/base.txt
pip-compile requirements/dev.in --output-file requirements/dev.txt
步骤 4:配置镜像源
# pip.conf
[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple/
trusted-host = pypi.tuna.tsinghua.edu.cn
步骤 5:配置 CI 缓存
# .github/workflows/ci.yml (关键片段)
jobs:
build:
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
cache-dependency-path: requirements/*.txt
- run: pip install -r requirements/dev.txt
步骤 6:开发工具使用 pipx 安装
pipx install black ruff poethepoet mypy pre-commit
步骤 7:定期自动升级依赖
# 使用 Dependabot 或 Renovate 自动创建 PR 升级依赖
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: pip
directory: "/requirements"
schedule:
interval: weekly
open-pull-requests-limit: 5
十三、核心要点总结
- requirements.txt 格式:支持版本约束(==、>=、~=、!=)、环境标记(sys_platform、python_version)、直接引用(@ URL/path)和 VCS 引用(git+https://),灵活应对各种依赖场景
- 环境标记:是编写跨平台 requirements 文件的关键——一个文件即可适配 Windows/Linux/macOS 和不同 Python 版本
- pip freeze vs pip list:freeze 输出可直接用于 requirements.txt 且包含传递依赖;list 用于日常查看环境状态和检查更新
- pip install 高级选项:--no-deps、--no-build-isolation、--find-links、--extra-index-url、--constraint 等选项在处理复杂依赖场景时不可替代
- pip-tools 双文件策略:requirements.in(声明顶层依赖)+ requirements.txt(锁定所有传递依赖),是可复现部署的黄金标准
- 依赖冲突解决:理解新解析器的回溯机制,善用 pipdeptree 和 pip check 诊断问题
- 私有包索引:devpi 适合团队、pypiserver 适合小规模、企业级可选 Artifactory/GitLab/AWS CodeArtifact
- pipx 隔离应用:CLI 工具用 pipx 安装,库依赖用 pip 在虚拟环境中安装
- Wheel vs sdist:Wheel 安装快、无需编译;sdist 是回退选项。企业私有源应同时提供两种格式
- 缓存管理:在 CI 中合理缓存 pip 缓存目录可显著加速构建流程
十四、进一步思考
从 pip 到现代包管理生态
深入了解 pip 之后,可以进一步探索以下工具和概念:
- Poetry:基于 pyproject.toml 的现代包管理工具,自动处理依赖锁定和环境管理
- PDM:Python 开发与依赖管理工具,支持 PEP 517/518 标准,锁定文件格式为二进制
- uv:Rust 编写的极速包管理工具(pip/venv 替代品),安装速度比 pip 快 10-100 倍
- Conda:不仅管理 Python 包,还能管理系统级二进制依赖(常用于数据科学)
- PEP 668:标记系统级 Python 环境,阻止 pip 直接安装到系统环境中,强制执行虚拟环境
学习路径建议
- 入门:掌握 requirements.txt 全语法 + pip freeze/list 差异
- 进阶:学会 pip-tools 工作流 + 多环境依赖管理 + 私有源配置
- 高级:理解 Wheel 构建与分发 + 自定义 pip 插件 + 依赖解析算法