AI 技术博客
返回首页
Python 基础 · 18 分钟阅读

包管理 pip 实战:安装、卸载、锁定与国内镜像源

## 引言 第 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` 四个依赖并校验版本兼容性;依赖找不到兼容版本时给出明确的冲突报错。**依赖冲突长什么样**?本机实测(模拟安装两个互相排斥的版本组合): ```text 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 篇的步骤),避免污染全局。为便于复现,先创建演示环境: ```bash python -m venv .venv && source .venv/bin/activate # Windows PowerShell: .venv\Scripts\activate ``` ### 安装与版本指定 默认装最新版: ```bash python -m pip install requests ``` 本机实测:安装后 `import requests` 并打印版本: ```text requests 2.34.2 OK ``` 指定精确版本(注意引号与 `==`): ```bash python -m pip install "requests==2.31.0" ``` 实测安装后版本变为 2.31.0。而**模拟安装**(不真正落盘,用于验证「能不能装」)是: ```bash python -m pip install --dry-run "requests==2.31.0" ``` 输出关键行: ```text Would install requests-2.31.0 ``` 检查某个包所有可用版本(实验性命令,会打印实验性警告,版本以本机为准): ```bash python -m pip index versions requests ``` ### 查看与卸载 `pip list` 列出全部已装包,本机(隔离环境)实测: ```text 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 <包名>` 查看单个包的详细信息(版本、依赖、主页等): ```bash python -m pip show requests ``` 卸载(`-y` 跳过确认): ```bash python -m pip uninstall -y requests ``` 卸载后 `import requests` 立刻报 `ModuleNotFoundError`——这是验证「真的卸干净了」的标准信号。 ### 锁定:requirements.txt 与 pip freeze 「锁定」指把当前环境的依赖及其精确版本记录下来,供以后重放。两条命令: ```bash python -m pip freeze # 输出所有包的 name==version 行 python -m pip freeze > requirements.txt # 写入文件 ``` 本机实测 `pip freeze` 输出: ```text certifi==2026.7.22 charset-normalizer==3.5.1 idna==3.19 requests==2.34.2 urllib3==2.7.0 ``` 注意两点:**freeze 出来的每一行都可用 `pip install -r` 原样输入**(这就是「锁定 → 重放」闭环);它是「输出格式」,不是「手写清单格式」。在新环境一次性重放: ```bash 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 提供了体检命令: ```bash python -m pip check ``` 在刚装好 requests 的干净环境里,本机实测输出: ```text No broken requirements found. ``` 若某个包要求的版本与已装包冲突,它会列出具体的「broken」信息。建议在每次安装/升级后跑一次,几秒换来的是一层安心。 ### 升级 pip 自身 pip 本身也是包,落后版本可能缺少新特性与依赖解析修复。升级它只需要: ```bash python -m pip install --upgrade pip ``` 在虚拟环境里执行时,升级的只是**当前环境**的 pip,不影响全局——又一次体现隔离的好处。建议每隔一段时间升级一次,版本号以 `python -m pip --version` 为准。 ### 配置国内镜像源 一次性用法(仅本次命令): ```bash python -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyyaml ``` 本机实测:通过清华镜像安装 PyYAML 成功,`pip show pyyaml` 显示 Version: 6.0.3——镜像内容与官方一致。 持久化配置(推荐,装一次永久生效): ```bash python -m pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple ``` 本机实测:命令把配置写入用户级配置文件(Windows 为 `%APPDATA%\pip\pip.ini`),内容如下: ```text [global] index-url = https://pypi.tuna.tsinghua.edu.cn/simple ``` 之后所有 pip 命令默认走镜像。查看当前全部生效配置: ```bash python -m pip config list ``` 想恢复官方源,执行 `python -m pip config unset global.index-url` 即可。 ### 离线安装:pip download + --no-index 内网/无网环境是真实场景,流程分两步:**联网机器下载、离线机器安装**。联网机器执行: ```bash python -m pip download requests -d wheels ``` 本机实测 `wheels/` 目录内容: ```text 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` 目录拷到离线机器后: ```bash 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`)。三条常用命令: ```bash python -m pip cache dir # 缓存目录在哪 python -m pip cache list # 缓存了哪些 wheel python -m pip cache info # 缓存占用统计 ``` 本机实测 `pip cache info` 的部分输出: ```text 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 的分工。至此,环境搭建与运行原理的「基建五件套」全部就位:语言选择、安装、原理、隔离、依赖。下一篇文章起,我们将走进交互式编程——让命令行成为你的第二大脑。 ## 练习与思考题 1. 在虚拟环境中执行 `python -m pip freeze > requirements.txt`,然后新建第二个虚拟环境,用 `python -m pip install -r requirements.txt` 重放,对比两个环境的 `pip list`。(答案导向:包名与版本逐条一致。) 2. 用 `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`。) 3. 写出 `requests~=2.31.0` 与 `requests==2.31.*` 展开后的等价比较式,说明它们的区别在哪。(答案导向:前者是 `>=2.31.0,==2.*`,允许 2.x 内所有大于等于 2.31.0 的版本;后者只允许 2.31.x 补丁版本。) 4. 思考题:为什么 `pip freeze` 会同时锁住 `certifi`、`urllib3` 等「你从没直接装过」的包?(答案导向:它们是 requests 的传递依赖,间接引入;锁定它们才能保证重放环境的字节级一致——这正是「可复现构建」的基础。)