Conda 包管理:安装、更新、卸载包,指定版本与渠道

聊 Conda,最核心的就是怎么管包。说白了,它就是你的 Python 环境管家。装什么、装哪个版本、从哪装,都得跟它交代清楚。我这么多年用下来,感觉 Conda 包管理这块坑不少,但摸清楚了,其实特顺手。

你想想看,一个项目一套环境,每个环境里几十上百个包。要是自己手动 pip 一个个装,版本冲突能让你怀疑人生。Conda 刚性依赖解析,这点真的省心。但省心归省心,你得知道怎么正确指挥它。

查找包:你得先知道有啥可装

动手装之前,我习惯先查一下。看看包名叫啥、有哪些版本、从哪个渠道来的。这一步看似多余,但能省掉不少后面改来改去的麻烦。

我常用的查找命令就这几个:

# 模糊搜索,看名字里带 numpy 的所有包
conda search numpy

# 搜特定版本,比如只要 1.21 系列的
conda search numpy=1.21

# 限定渠道搜,比如只搜 conda-forge 里的
conda search -c conda-forge numpy

# 看详细信息,包括依赖、大小、渠道
conda search --info numpy

我在项目中遇到过一次,搜到一个包名带着 -py38_0 后缀,其实那是为 Python 3.8 编译的版本。你要是拿它往 Python 3.11 环境里装,大概率要出事。所以搜的时候多看一眼平台标签,没坏处。

小技巧:用 conda search --envs 可以只看当前环境里能装的包,对于大项目多环境管理很实用。

安装包:基础操作,但细节多

安装这事,看着就一行命令。但这里面的道道,我敢说很多老手也未必全清楚。

最基本的安装命令:

# 装最新版本,啥都不指定
conda install requests

# 装指定版本
conda install numpy=1.21.5

# 装版本范围,比如 1.20 到 1.22 之间随意
conda install 'numpy>=1.20,<1.23'

# 从特定渠道装
conda install -c conda-forge pandas

# 同时装多个包
conda install numpy pandas scipy

# 装的时候只看依赖,不真装(dry-run)
conda install numpy --dry-run

你可能会问,为什么非得指定版本?我碰过一次,项目代码里用了 NumPy 1.22 新增的 API,结果 CI 环境默认装了个 1.19 的旧版,直接报错。你想想看,要是当初装的时候指定了 numpy>=1.22,这问题压根不存在。

还有一个我个人的习惯:装包前一定跑 --dry-run。看一眼它会改什么东西,会不会更新一些不该动的包。特别是生产环境,这一下能拦住不少事故。

注意:用 -c 指定渠道时,渠道优先级会影响解析结果。比如 conda install -c conda-forge tensorflow 可能和默认渠道的版本机制不一样。我曾经遇到过,混装两个渠道的包,最后环境一团乱。建议一个项目锁定一到两个主渠道。

更新包:不要手欠全量更新

更新包是另一个容易翻车的地方。很多人上来就 conda update --all,然后重启环境发现包全炸了。嗯,这里要注意。

我推荐这样更新:

# 只更新指定包,最安全
conda update numpy

# 更新到指定版本
conda update numpy=1.22.0

# 查看哪些包可以更新(预览模式,不真干)
conda update --all --dry-run

# 只更新主渠道的包,不碰其他渠道的
conda update --all -c defaults

我曾经手快了一把,在项目中间阶段跑了 conda update --all,结果 scipy 从 1.7 跳到了 1.9,API 变动导致整个数据管道断掉,浪费了大半天回滚。从那以后,我更新包必定先做两步:第一步 conda list --explicit > env_backup.txt 备份环境;第二步 conda update xxx --dry-run 看影响范围。

卸载包:清理干净才算完

卸载看上去比安装简单,但「残余依赖」是个大坑。你卸了一个包,它依赖的那些库还在环境里,慢慢就变成无人认领的垃圾。

标准卸载命令:

# 卸掉指定包
conda remove numpy

# 卸掉多个包
conda remove numpy pandas

# 连依赖一起卸(小心,可能波及别的包)
conda remove numpy --force-remove

我个人的标准流程是:卸载后跑一下 conda clean --all,清掉缓存和没用的 tarball。要是发现环境里多了不知道用不用的包,就用 conda list --revisions 看看历史版本,然后 conda install --revision N 回滚到干净状态。

避坑指南:conda remove 默认只卸指定的包,不会自动清理那些被它带进来、但现在没人用的依赖包。要彻底清理,建议用 conda remove package_name --force-remove,或者干脆重建环境。我踩过这个坑,环境里积累了几十个孤儿包,最后只能翻新环境。

指定渠道:包的来源决定可靠性

Conda 的渠道,说白了就是包从哪里来。默认的 defaults 渠道是 Anaconda 官方的,比较稳定但更新慢。conda-forge 是社区维护的,更新快、包全,但偶尔会有小版本踩坑。

渠道优先级的影响:

操作 命令示例 渠道行为
一次性指定 conda install -c conda-forge xxx 本次安装优先用 conda-forge
永久添加 conda config --add channels conda-forge 该渠道加入优先级列表,之后任何操作都先用它
设置优先级 conda config --set channel_priority strict 严格按渠道顺序解析,不会串着装
查看当前渠道 conda config --show channels 列出所有渠道及优先级顺序

你可能会问,渠道到底怎么选?我个人推荐:生产环境用 defaults 为主,conda-forge 为辅。除非某个包只在 conda-forge 上有最新版,否则不要轻易混装。我把渠道优先级设为 strict,这样 Conda 不会自作聪明地跨渠道拼装,解析结果稳定很多。

实践建议:跑 conda config --show default_channels 看看你的默认渠道列表。如果发现有 r、bioconda 之类你根本用不到的渠道,建议删掉,减少解析时的干扰项。

版本锁定:这才是环境复现的关键

回到我们课程的核心——环境锁定。包管理里最容易出问题的,就是「当前版本」和「锁定版本」对不上。

我建议每次环境稳定后,立刻做版本锁定。不光锁顶级依赖,连传递依赖也锁。具体做法:

# 导出当前环境的精确版本列表
conda list --explicit > env_snapshot.txt

# 导出含渠道信息的版本列表
conda env export > environment.yml

# 查看某个包和它的所有依赖版本(传递依赖)
conda tree numpy

为什么锁版本这么重要?我在项目中遇到过一次,团队里有人新装了包,更新了几个隐式依赖。其他人拉代码、重建环境后发现数据流程结果不一样。查了两天才发现是 scikit-learn 的某个小版本更新改了默认参数。要是当时锁了版本,这种事根本不会发生。

锁版本锁的是什么?说白了就是锁「确定性」。你确定你写的代码在别人电脑上、在 CI 里、在生产环境上,跑的依赖都一样。Conda 的 explicit 格式最靠谱,因为它连包名的内部哈希都锁进去了。

# explicit 格式示例,每行都是精确的 URL + 哈希
https://conda.anaconda.org/conda-forge/linux-64/numpy-1.21.5-py39h2f0e1d8_0.tar.bz2#a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6

你想想看,这个 URL 里不光有版本号,还有编译标识、Python 版本标识、甚至构建序号。只要这个文件没变,你装出来的包一定和我的一样。这就是我们想要的环境复现。

特别注意:conda env export 导出的 yml 文件,默认只锁了显式安装的版本,不会锁传递依赖。要锁全量,必须用 --from-history 配合 conda list --explicit 双保险。我以前在这上面吃过亏,以为导出的 yml 就够了,结果别人重建环境时版本漂了一丢丢,项目差点延期。

好了,Conda 包管理的这几个基本功——安装、更新、卸载、渠道、版本锁定——你都了解了吧?它们看起来零散,但组合起来就是环境复现的基石。我习惯在项目一开始就把这些操作变成标准流程,后面几乎不用再操心。

下面这张图把包管理的主要操作串联在一起,帮你建立整体感:

Conda 包管理核心操作图 Conda 包管理 安装 (install) 更新 (update) 卸载 (remove) 渠道与版本 最新版本 指定版本 版本范围 指定包更新 全量预览 渠道限制 单包移除 连带依赖 回滚历史 defaults 渠道 conda-forge 渠道 版本锁定 操作顺序建议 搜索 → 指定版本+渠道安装 → 环境稳定后锁版本 → 后续用锁定的版本更新/回滚

这张图把四个核心操作连起来了。你仔细看,无论是安装、更新还是卸载,最后都会落到「渠道与版本」这个节点上。这也就解释了为什么我们这一章要把版本锁定跟包管理放在一起讲——它们是同一个硬币的两面。