引言
第 4 篇我们解决了「每个项目一个干净的屋子」——虚拟环境。但屋子里要摆什么家具、家具从哪来、怎么保证下次买到的家具和这次一模一样?这是这一篇的主题:包管理。工具是 Python 官方包安装器 pip(Pip Installs Packages 的递归缩写,也是「优先用 pip」这句骂名——不过这并不妨碍它好用)。
读者应先掌握第 4 篇的全部内容:创建并激活虚拟环境、python -m pip 的调用方式、site-packages 的隔离原理。本文目标:能熟练完成「查找 → 安装 → 验证 → 卸载」的日常循环,能把依赖锁定成 requirements.txt 并在新机器上重放,能配置国内镜像源解决下载慢问题,能在无网络环境用离线包安装。全部命令都基于 pip 25 在本机实测验证(你的 pip 版本以 python -m pip --version 为准,命令一致)。
概念与原理
pip 从哪来、装的是什么
pip 从索引(index)下载包,默认索引是 PyPI(Python Package Index,Python 包索引,第 1 篇介绍过,托管 50 万+ 包)。pip 能安装两种分发格式:
- wheel(
.whl):打包好的「成品」,又分纯 Python 版(如requests-2.34.2-py3-none-any.whl,任何平台通用)与带编译产物的平台版(如charset_normalizer-3.5.1-cp312-cp312-win_amd64.whl,文件名里的cp312表示 CPython 3.12、win_amd64表示 64 位 Windows)——这类 wheel 是预编译的,装完即可用,不需要本机编译器; - sdist(源码包,
.tar.gz):源码形式,安装时若没有对应 wheel,pip 会现场构建——这要求本机有 C 编译器,也是「装某个包报编译错误」的根源(解决方案通常是用镜像源或换用有 wheel 的版本)。
pip 安装时自动解析依赖树:装 requests 会同时装 certifi、charset-normalizer、idna、urllib3 四个依赖并校验版本兼容性;依赖找不到兼容版本时给出明确的冲突报错。依赖冲突长什么样?本机实测(模拟安装两个互相排斥的版本组合):
ERROR: Cannot install idna==2.0 and requests==2.31.0 because these package versions
have conflicting dependencies.
ERROR: ResolutionImpossible: for help visit
https://pip.pypa.io/en/latest/topics/dependency-resolution/#dealing-with-dependency-conflicts
ResolutionImpossible 的意思是:在当前版本约束下,pip 无法找到一组彼此兼容的版本。解法不是绕开报错,而是放宽其中一个约束(本例 requests>=2.31.0 要求 idna>=2.5,<4,强行装 2.0 当然冲突)。看懂这类报错,是排查「装不上」问题的一半功力。
版本说明符:把「装哪个版本」讲清楚
pip 的版本说明符(version specifier)语法如下,都放在引号里传给命令:
| 写法 | 含义 | 示例 |
|---|---|---|
== | 精确锁定 | requests==2.31.0 |
>= / <= | 大于等于 / 小于等于 | requests>=2.28 |
~= | 兼容版本:允许同次版本内升级 | requests~=2.31.0 等价 >=2.31.0,==2.* |
!= | 排除某版本 | requests!=2.30.0 |
* | 通配 | requests==2.31.* |
组合用逗号:"requests>=2.28,<3.0"。安装时命令名与说明符之间不要留空格(requests ==2.31.0 是错误写法),完整版用引号包住 "requests==2.31.0",避免被 shell 的字符解释干扰。
为什么需要国内镜像源
pip 默认直连 PyPI(位于国外),国内网络环境下经常慢、超时、断流。镜像源(mirror)是 PyPI 的定时同步副本,放在国内服务器,访问快得多。几个真实可用的镜像(均长期维护,选一个即可,教程示例用清华 TUNA 镜像):
- 清华 TUNA:
https://pypi.tuna.tsinghua.edu.cn/simple - 阿里云:
https://mirrors.aliyun.com/pypi/simple - 腾讯云:
https://mirrors.cloud.tencent.com/pypi/simple
镜像内容与 PyPI 基本一致(同步有时差,新发布的包可能延迟数小时到一天),下载源改变不影响包的内容——包文件有哈希校验。同理,公司内网也会自建 PyPI 私有源(配合制品库工具如 devpi、Nexus),用法完全相同:把 URL 换成内网地址即可——所以「换源」这个技能一次学会,处处通用。
配置优先级:三处设置谁说了算
pip 的配置来源按优先级从高到低:命令行参数 → 环境变量(PIP_INDEX_URL 等 PIP_* 系列)→ 配置文件。配置文件的位置:Windows 在 %APPDATA%\pip\pip.ini,macOS/Linux 在 ~/.config/pip/pip.conf(或 ~/.pip/pip.conf)。这也解释了为什么「命令行加了 -i 有效、换台机器又变慢」——命令行参数只对本条命令生效,持久化要靠配置文件。
操作/实现
下面的操作请在已激活的虚拟环境中进行(第 4 篇的步骤),避免污染全局。为便于复现,先创建演示环境:
python -m venv .venv && source .venv/bin/activate # Windows PowerShell: .venv\Scripts\activate
安装与版本指定
默认装最新版:
python -m pip install requests
本机实测:安装后 import requests 并打印版本:
requests 2.34.2 OK
指定精确版本(注意引号与 ==):
python -m pip install "requests==2.31.0"
实测安装后版本变为 2.31.0。而模拟安装(不真正落盘,用于验证「能不能装」)是:
python -m pip install --dry-run "requests==2.31.0"
输出关键行:
Would install requests-2.31.0
检查某个包所有可用版本(实验性命令,会打印实验性警告,版本以本机为准):
python -m pip index versions requests
查看与卸载
pip list 列出全部已装包,本机(隔离环境)实测:
Package Version
------------------ ---------
certifi 2026.7.22
charset-normalizer 3.5.1
idna 3.19
pip 25.0.1
requests 2.34.2
urllib3 2.7.0
pip show <包名> 查看单个包的详细信息(版本、依赖、主页等):
python -m pip show requests
卸载(-y 跳过确认):
python -m pip uninstall -y requests
卸载后 import requests 立刻报 ModuleNotFoundError——这是验证「真的卸干净了」的标准信号。
锁定:requirements.txt 与 pip freeze
「锁定」指把当前环境的依赖及其精确版本记录下来,供以后重放。两条命令:
python -m pip freeze # 输出所有包的 name==version 行
python -m pip freeze > requirements.txt # 写入文件
本机实测 pip freeze 输出:
certifi==2026.7.22
charset-normalizer==3.5.1
idna==3.19
requests==2.34.2
urllib3==2.7.0
注意两点:freeze 出来的每一行都可用 pip install -r 原样输入(这就是「锁定 → 重放」闭环);它是「输出格式」,不是「手写清单格式」。在新环境一次性重放:
python -m pip install -r requirements.txt
本机实测:新建第二个虚拟环境执行上述命令后,pip list 与源环境完全一致(requests 2.34.2、pip 25.0.1 及其余依赖逐条对齐)。工程上的分工建议:requirements.txt 用 pip freeze 生成,做完全锁定(连传递依赖一起锁);若想只列顶层依赖、让 pip 自由解析,可另写 requirements.in 并结合 pip-tools 等工具——那是进阶话题,本篇先用好 freeze。另一个惯例:运行依赖与开发依赖分开,例如测试/构建工具写在 requirements-dev.txt 中(文件里可用 # 注释说明每个包的用途),两条清单各自锁定、分别重放,避免把开发工具装进生产环境。
依赖一致性体检:pip check
依赖锁定之后,环境里是否真的「两两兼容」?pip 提供了体检命令:
python -m pip check
在刚装好 requests 的干净环境里,本机实测输出:
No broken requirements found.
若某个包要求的版本与已装包冲突,它会列出具体的「broken」信息。建议在每次安装/升级后跑一次,几秒换来的是一层安心。
升级 pip 自身
pip 本身也是包,落后版本可能缺少新特性与依赖解析修复。升级它只需要:
python -m pip install --upgrade pip
在虚拟环境里执行时,升级的只是当前环境的 pip,不影响全局——又一次体现隔离的好处。建议每隔一段时间升级一次,版本号以 python -m pip --version 为准。
配置国内镜像源
一次性用法(仅本次命令):
python -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyyaml
本机实测:通过清华镜像安装 PyYAML 成功,pip show pyyaml 显示 Version: 6.0.3——镜像内容与官方一致。
持久化配置(推荐,装一次永久生效):
python -m pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
本机实测:命令把配置写入用户级配置文件(Windows 为 %APPDATA%\pip\pip.ini),内容如下:
[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
之后所有 pip 命令默认走镜像。查看当前全部生效配置:
python -m pip config list
想恢复官方源,执行 python -m pip config unset global.index-url 即可。
离线安装:pip download + —no-index
内网/无网环境是真实场景,流程分两步:联网机器下载、离线机器安装。联网机器执行:
python -m pip download requests -d wheels
本机实测 wheels/ 目录内容:
certifi-2026.7.22-py3-none-any.whl
charset_normalizer-3.5.1-cp312-cp312-win_amd64.whl
idna-3.19-py3-none-any.whl
requests-2.34.2-py3-none-any.whl
urllib3-2.7.0-py3-none-any.whl
download 会把目标包连同依赖一起取出。把 wheels 目录拷到离线机器后:
python -m pip install --no-index --find-links wheels requests
--no-index 禁止访问任何索引,--find-links 指定本地目录。本机实测:全新虚拟环境中安装成功,requests.__version__ 输出 2.34.2。注意:平台特定 wheel(如上面的 charset_normalizer-...-win_amd64.whl)只适用于相同架构——下载时若知道目标平台不同,应加 --platform 或直接下载 sdist,这属于进阶话题。
本地缓存:pip cache
下载过的包不会重复下——pip 把 wheel 缓存到本地(本机实测缓存目录:c:\users\<用户名>\appdata\local\pip\cache)。三条常用命令:
python -m pip cache dir # 缓存目录在哪
python -m pip cache list # 缓存了哪些 wheel
python -m pip cache info # 缓存占用统计
本机实测 pip cache info 的部分输出:
Package index page cache size: 110.5 MB
意义有二:一是重装/换环境时命中缓存会明显变快(对有网但慢的环境是安慰,对断网重装环境是救命);二是当你怀疑「缓存里的旧版本污染了新安装」时,python -m pip cache purge 清空重来——不过这极少必要,pip 的缓存按内容寻址、不会悄悄投毒。
易错点与陷阱
易错点 1:pip 装到了「错误的解释器」。 未激活环境(或 pip 与 python 分属不同解释器)时执行 pip install,包进了全局或另一个环境,随后 import 失败。对策就是第 4 篇练过的固定搭配:始终 python -m pip ...,且装前确认 which python 在 .venv 内。
易错点 2:把 pip freeze > requirements.txt 的产物再喂回 pip install -r,导致「requirements.txt 引用自己」的死锁。 正确的读写方向是:freeze 是输出(锁定现状),-r 是输入(重放)。常见翻车现场是项目里只放了一个自产自销的 requirements.txt,新人 pip install -r requirements.txt 装不上任何东西——请检查文件里是否只有 requirements.txt==... 这种行。
易错点 3:锁定了却用 pip install -U(或升级命令)悄悄破坏锁定。 -U 会把包升级到最新版本,如果随后发生兼容问题,而你手头的 requirements.txt 还是旧的,环境就与清单脱节了。规则:要动版本就改清单(或重新 freeze),要让环境与清单一致就用 -r。
易错点 4:镜像配置「这次有效、下次失效」。 命令行 -i 只对本条命令生效;新人常因此以为镜像没配好。持久化必须走 pip config set global.index-url ...(或环境变量 PIP_INDEX_URL),并用 pip config list 验证。
易错点 5:以为 pip list 等于 pip freeze。 两者列出相同的包集合,但格式与用途不同:list 是人类可读的表格(含 pip 自身),freeze 是机器可重放的 name==version 行(不含不可重放的条目)。写脚本、做重放请用 freeze。
小结
本文是 pip 的完整实战:它的来源与两种分发格式(wheel/sdist)、版本说明符语法(==、~=、>= 等)、依赖自动解析;安装/精确版本/模拟安装/查看/卸载的日常循环;用 pip freeze > requirements.txt 锁定、用 pip install -r 重放;清华、阿里、腾讯三个国内镜像源的一次性与持久化配置;以及 pip download + --no-index 的离线安装链路。五个易错点提醒:解释器对应关系、freeze 与 -r 的方向、-U 对锁定的破坏、镜像配置的持久化、list 与 freeze 的分工。至此,环境搭建与运行原理的「基建五件套」全部就位:语言选择、安装、原理、隔离、依赖。下一篇文章起,我们将走进交互式编程——让命令行成为你的第二大脑。
练习与思考题
- 在虚拟环境中执行
python -m pip freeze > requirements.txt,然后新建第二个虚拟环境,用python -m pip install -r requirements.txt重放,对比两个环境的pip list。(答案导向:包名与版本逐条一致。) - 用
python -m pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple配置镜像,再用python -m pip config list验证;回答配置文件在 Windows 与 macOS/Linux 上的路径。(答案导向:Windows%APPDATA%\pip\pip.ini;macOS/Linux~/.config/pip/pip.conf。) - 写出
requests~=2.31.0与requests==2.31.*展开后的等价比较式,说明它们的区别在哪。(答案导向:前者是>=2.31.0,==2.*,允许 2.x 内所有大于等于 2.31.0 的版本;后者只允许 2.31.x 补丁版本。) - 思考题:为什么
pip freeze会同时锁住certifi、urllib3等「你从没直接装过」的包?(答案导向:它们是 requests 的传递依赖,间接引入;锁定它们才能保证重放环境的字节级一致——这正是「可复现构建」的基础。)