3. 包管理基础
说起包管理,这其实是每次做环境时最基础的活儿。但在 Conda 生态里,基础不等于简单。很多人在入门时就养成了坏习惯——今天 pip 装一个,明天 conda 装一个,最后环境乱成一锅粥。
我自己早年间就吃过这个亏。有一次项目赶进度,随便往 base 环境里塞包,结果某个依赖版本冲突,整整排查了一天。从那以后,我对包管理的每个动作都格外注意。
说白了,包管理就是三件事:装什么、怎么升级、怎么清理干净。我们一个一个来看。
安装包:pip vs conda
安装一个包,最直接的方式就是:
conda install 包名
或者:
pip install 包名
但这两者背后的逻辑完全不同。Conda 是环境级的包管理器,它不光管 Python 包,还能管 C 库、动态链接库这些东西。而 pip 只管 Python 包,它不关心系统的二进制依赖。
举个例子,你想装一个做图像处理的 opencv。用 pip 装:
pip install opencv-python
这条命令只拉了一个 Python wheel 包。但如果你的系统缺少 opencv 所需要的底层库(比如 ffmpeg),那可能运行时就报错。
用 conda 装:
conda install opencv
Conda 会自动把背后的 libopencv、ffmpeg 等 C 库一起装好。这就是我为什么建议优先用 conda——省心,尤其在内网环境或服务器上。
安装指定版本
在实际项目中,很多时候不能装最新版,得锁定某个特定版本。比如:
conda install numpy=1.21.0
或者用 pip:
pip install numpy==1.21.0
我曾经在一个生产环境里,因为装了最新的 pandas 2.0,结果好几个旧脚本报错。那会儿我就明白了——版本锁定不是保守,而是负责。后面讲版本锁定策略时,我们还会深入聊这个。
包更新:要不要升级?
更新包听起来简单,但在项目里要慎重。命令就两行:
conda update 包名
conda update --all
或者:
pip install --upgrade 包名
你想想看,更新一个包,可能带来依赖链上的连锁反应。我记得有一次,我随手更新了 requests 库,结果把整个环境的 urllib3 版本都带偏了,导致另一个核心模块没法正常发送 HTTP 请求。
所以我的原则是:非必要不全局更新。真要更新,先读 release notes,再看依赖变化。最好在隔离的环境里测试完再推广。
conda update --all 可能会把 Anaconda 自身的核心包也升级,有时反而导致环境不稳定。在正式项目里,我基本不用这个命令。
卸载包:清理要彻底
安装容易,卸载也不难:
conda remove 包名
或者:
pip uninstall 包名
但这里有一个坑——依赖不会自动跟随删除。什么意思呢?你装 A 包时,自动装上了它的依赖 B。等你卸载 A 包,B 包仍然留在环境里。久而久之,环境里就会有一堆「孤儿包」。
我踩过一个坑:在一个长期维护的项目里,积攒了很多无用的遗留包,最终导致 conda solve 解析依赖时越来越慢,甚至出现莫名其妙的不一致。后来我专门写了个脚本,定期清理没有反向依赖的包。
conda list --revisions 查看环境变更记录,反向追踪哪些包可以一并清理。
查看已安装的包列表
查看当前环境里的包,应该是每天都要做的操作:
conda list
这条命令会输出一个表格,包含包名、版本、来源渠道。输出格式是这样的:
# packages in environment at /opt/conda/envs/my_project:
#
# Name Version Build Channel
numpy 1.21.0 py39h20f2e39_0 conda-forge
pandas 1.3.0 py39h1d20d9e_0 conda-forge
python 3.9.6 h12debd9_0 defaults
...
如果你用的是 pip,查看命令是:
pip list
或者想看更详细的信息:
pip show 包名
我个人习惯是用 conda list 作为主要查看工具,因为它能同时显示从 conda 和 pip 安装的包(前提是你在 conda 环境内)。不过要留意,单纯用 conda list 看到的 pip 包信息可能不全,必要时还是得用 pip list 补一下。
| 功能 | Conda 命令 | Pip 命令 |
|---|---|---|
| 安装 | conda install | pip install |
| 指定版本安装 | conda install 包名=版本 | pip install 包名==版本 |
| 更新 | conda update | pip install --upgrade |
| 卸载 | conda remove | pip uninstall |
| 查看列表 | conda list | pip list |
环境快照:export 与 list 的巧妙运用
查看包列表,不只是为了看一眼。更重要的是记录当前环境的精确状态。这在后面做版本锁定时至关重要。
把你当前环境的包列表导出成文件:
conda list --export > requirements.txt
或者更精确的方式——只记录你显式安装的包,不记录自动依赖:
conda list --explicit > spec-file.txt
这个 spec-file.txt 就是环境快照。你可以用它重建一个一模一样的环境:
conda create --name new_env --file spec-file.txt
嗯,这里要注意:--explicit 生成的 URL 中包含 build 信息和渠道,跨平台可能会失效。如果你的团队需要在多个操作系统上复现环境,建议用 conda env export 导出 yml 格式。
conda env export > environment.yml
这种 yml 文件是跨平台的,后续直接拿来创建环境就好。
conda list 的终端输出截图当记录。那东西没法回放。一定要保留 --export 或 env export 生成的可重入文件,才能确保环境能一键复现。
知识结构图:包管理操作全流程
下面这张图总结了本章的核心逻辑,从依赖解析到最终环境激活,每一步都和前后的审计与版本锁定策略有关。
日常操作 checklist
为了不遗漏关键步骤,我整理了一个简洁的 checklist。做环境管理时,按照这个顺序过一遍就好:
- 安装新包前:先确认环境中是否存在,避免重复安装。
- 指定版本:用
==或=精确锁定版本。 - 更新时:不要用
--all,改为逐个更新并检查依赖。 - 卸载后:检查是否有遗留依赖,可以考虑
conda clean --packages清理缓存。 - 导出快照:每次重大变更后,运行
conda env export > snapshot.yml。 - 审计:定期用
conda list --revisions回溯变更记录。
这些看起来都是基础操作,但越基础的东西,越容易在日常中出错。我在项目里见过太多因为「装一个包没指定版本」导致环境不可复现的案例。
所以,从这一章开始,我希望你能养成习惯:每次安装或更新包,都把它当作一次审计动作来做。记录、对比、锁定——这是后面所有容器化和审计工作的基石。这一套基础打牢了,后续讲版本锁定策略时会顺很多。