ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Conda包管理全流程:安装、卸载、升级与查看实战指南

Conda包管理全流程:安装、卸载、升级与查看实战指南 1. 先想清楚为什么包管理要用 conda而不是 pip 一把梭conda安装包、卸载包、升级包、查看包信息这四个动作看着就是四条命令的事但真正做过环境维护的人都知道坑全在细节里。我见过太多人conda create用得飞起一到这个包版本不对想换掉装到一半想退回去环境里到底装了啥说不清的场合就开始凭感觉乱敲最后把 base 环境堆成一个谁都不敢动的垃圾场。这篇就把这套闭环从头到尾捋一遍装之前怎么想、装的时候怎么选、卸的时候怎么退、升的时候怎么保命、查的时候怎么看懂输出。不管你是刚接触 conda 的新手还是用了两三年但一直靠pip install打补丁的老手都能从里面挑到能直接抄的东西。先说一个最基本的定位问题。conda 不是一个 Python 包管理器的替代品它是一个跨语言的二进制包管理器加环境管理器顺带管 Python。这个定位决定了它后面所有的设计取舍也决定了你该在什么时候用它、什么时候绕开它。很多人对 conda 的抱怨其实都源于把它当成慢一点的 pip来用那确实处处别扭。1.1 conda 管的东西比 pip 多一层pip 装的是 Python 包落到site-packages目录里管的是Python 层面能不能 import 到。conda 装的是二进制包Python 包只是其中一类除此之外它还能装编译器、数学库、音视频库、GPU 运行时、甚至 R 语言和 Node.js。这个差别在装 numpy 的时候最明显conda 给你的 numpy 是预编译好、并且链接了特定 BLAS 实现的二进制而 pip 给你的 wheel 里链接的 BLAS 未必是你这台机器最优的那一个。我第一次真切感受到差距是装 PyTorch 的时候。用 conda 装pytorch、torchvision、cudatoolkit它会一次性把 CUDA 运行时的依赖全部拉齐版本互相咬合装完直接能跑用 pip 装你得自己确认系统里 CUDA 驱动版本、cuDNN 版本、torch 版本三者能不能对上对不上就是一句CUDA error: no kernel image is available然后开始漫无目的地降级。所以但凡涉及包本身依赖系统级库的场景conda 的价值是压倒性的。1.2 什么时候干脆交给 pip但 conda 不是万能的。它的 channel 库里包的数量远不如 PyPI很多新发布的包、小众包、公司内部包conda 里搜不到那就只能 pip。判断标准很简单先用conda search搜一次搜得到就走 conda搜不到就走 pip。需要特别提醒的是混用的顺序。正确姿势是先用 conda 把所有能装的装完最后用 pip 补那几个 conda 里没有的并且装完不要再回头动 conda。原因在于 conda 解依赖时看不到 pip 装进来的包pip 也不知道 conda 的依赖图两边各算各的最后可能出现同一个包被两个管理器各装了一份import的时候谁在前谁生效出问题极难排查。优先 condanumpy、scipy、pandas、pytorch、opencv、jupyter、cudatoolkit、gcc优先 pip刚发布不到几周的包、纯 Python 工具、conda channel 里搜不到的私有包绝对避免同一个包先用 conda 装了又用 pip 覆盖一遍1.3 环境隔离这件事不隔离的代价很大不建环境最直接的后果是版本打架。base 环境里塞了三个项目的依赖A 项目要 pandas 1.5B 项目要 pandas 2.0你只能反复卸载重装每次切项目都要等几分钟还可能顺手把 A 项目的其他依赖一起卸掉。第二个后果更隐蔽某些包的安装会顺带升级它的依赖链升着升着把某个原本能跑的东西弄坏了而你根本想不起来是哪次操作干的。我个人的习惯是一个项目一个环境环境名直接用项目英文简写加 Python 版本比如crawler311、nlp39。这样半年后回头看conda env list的输出一眼就知道哪个环境是干什么的也敢放心删。环境名里带版本号还有个好处Python 3.9 和 3.11 的项目环境不会因为名字撞车而互相覆盖。2. 动手之前把 conda 装好、配好、看清楚装 conda 本身不复杂但装完之后的三件事决定了你后面几个月的使用体验装在哪、走哪个源、shell 有没有初始化。这三件里任何一件没做对都会在你某天敲下conda activate的时候突然跳出来给你一巴掌。2.1 安装路径与发行版选择目前主流的两个方向是 Anaconda 和 Miniconda。Anaconda 是全家桶装完自带几百个常用的数据科学包代价是几个 GB 的磁盘和一大坨你可能永远不用的东西。Miniconda 只有一个 conda 加 Python 本身几百 MB需要什么自己装。除了教学场景我建议一律用 Miniconda环境干净、升级快、出问题好排查。安装路径上有一条经验不要装在需要管理员权限的目录路径里也不要带空格和中文。Windows 上默认会往用户目录装这是对的Linux 上不要装到/opt或者/usr/local装到~/miniconda3这种地方。原因有两个一是后面conda install要频繁写文件权限问题会带来一堆莫名其妙的失败二是路径带空格时某些第三方工具调用 conda 的 Python 会因为没加引号而把路径截断报出一个跟真实原因毫无关系的错。安装时那个 Add to PATH 的勾选Windows 上默认不勾这是故意的。它希望你走conda init的方式而不是硬改 PATH否则容易和系统里已有的 Python 打架。2.2 换源把下载速度拉起来默认源在境外国内直接用的体验是装一个 pytorch 能等到打瞌睡中途断流还会导致包下载不完整、解压报错。所以装完第一件事就是换到国内的开源镜像站。以清华镜像为例对应的配置文件是用户目录下的.condarcconda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/ conda config --set show_channel_urls yesshow_channel_urls yes这行的作用是让conda list输出里多一列Channel能看出每个包是从哪个源来的。排查为什么这个包装的不是我想要的版本时这列信息非常关键。写完可以conda config --show channels确认顺序channels 是自上而下按优先级匹配的所以放进来的顺序会影响最终选中的包。有一点要提醒如果只是想临时用某个源装一个包别改全局配置直接在命令后面加-c conda-forge就行用完即走不污染主配置。2.3 conda init 与那个经典报错很多人第一次用会遇到conda error: run conda init before conda activate。这个报错的意思不是 conda 坏了而是你的 shell 还不知道 conda 的存在——conda activate依赖 shell 函数的注入而这个注入是靠conda init写进.bashrc或.zshrc的。解决办法就是按它说的做然后重开一个终端conda init bash # zsh 用户写 conda init zsh重开之后命令行提示符前面会出现(base)说明初始化成功。如果不想要每次开终端都自动进 base我本人不喜欢会拖慢启动可以关掉conda config --set auto_activate_base false另一个高频问题是conda 不是内部或外部命令这基本等于 PATH 没配好。要么是安装时没勾 Add to PATH 又没跑conda init要么是装完之后没重开终端。这种情况不用重装找到安装目录下的condabin手动加进环境变量或者直接跑一次conda init再重开终端就行。2.4 先看清楚自己手里有什么动手装包之前先养成确认当前状态的习惯这几条命令花不了几秒但能避免装到别的环境去了这种低级错误conda --version # conda 自身版本 conda info # 当前环境、平台、channel 等全局信息 conda env list # 所有环境及路径等价于 conda info --envs conda list # 当前环境已安装的包conda info的输出里有几行值得盯active environment告诉你现在在哪个环境base environment是 base 的路径platform是系统架构。我遇到过好几次同事说我明明装了包怎么 import 不到最后发现是conda activate没生效命令实际打进了 base 环境。3. 安装包从搜索到落地的完整链路装包是这四个动作里最常做、也最容易做错的一步。做错的形式不是报错而是装上了但版本不对装到别的环境去了依赖被悄悄换掉了。下面按顺序拆。3.1 先搜后装conda search 的使用conda search是装包前最值得花时间的一步它能告诉你这个包在哪个 channel 有、有哪些版本、支持哪些平台。但这里有个近几年的重要变化需要知道conda 23.9 之后conda search默认只搜默认源不再自动把所有已配置 channel 都搜一遍因为全搜一遍太慢了。所以如果你确定包在 conda-forge 上就得手动指定conda search -c conda-forge numpy conda search -c conda-forge numpy1.24conda search的输出是一张表列名包括 Name、Version、Build、Channel、Platform。后面的 Build 字符串看起来像乱码其实有信息量py311h1234abc_0里的py311表示这个包是为 Python 3.11 编译的h1234abc是构建哈希最后的_0是构建编号。选包时版本号相同的情况下优先选构建号大的。3.2 精确指定版本与来源的几种写法conda 的版本约束语法比 pip 宽松支持几种写法实际用途各不一样写法含义使用场景numpy由求解器选最合适的版本日常安装无特殊要求numpy1.241.24 系列的任意版本只锁大版本允许补丁更新numpy1.24.3精确到该版本复现问题、对齐他人环境numpy1.24,2区间约束明确知道 2.x 有破坏性变更numpy*py311*约束构建字符串指定 Python ABI 版本指定 channel 也有几种力度差别很大-c conda-forge是把 conda-forge 加入候选但仍可能从其他源拿包--override-channels -c conda-forge是只从 conda-forge 拿别的源一律不看。想要结果可预测就用后者。conda install --override-channels -c conda-forge numpy1.24.33.3 装之前先干跑一遍这是我压箱底的习惯也是我最推荐新人立刻养成的习惯加--dry-run简写-d先看一遍会发生什么。conda install -n myenv -c conda-forge pandas --dry-run它会完整走一遍依赖求解把将要安装哪些包、将要从哪些版本升级到哪些版本、哪些包会被降级全部打印出来但不实际写入。输出里最需要看的是三类动作install、update、downgrade。如果看到一堆 downgrade尤其是把你现有的核心包往下降那就该停下来想想了——这个包到底值不值得装。我有个真实例子装某个图像处理库时dry-run 显示它会把 numpy 从 1.26 降到 1.21。降完之后另一个依赖 numpy 2.x API 的模块直接跑不动。当时如果不看这一步直接回车后面就要花半小时排查一个完全无关的方向。3.4 装到指定环境而不是 baseconda install不带参数时默认装进当前激活的环境。如果你没激活任何环境那就是 base而往 base 里随手装东西是一个需要戒掉的习惯。稳妥的写法是每次都显式带上环境名conda install -n crawler311 requests如果目标是新建环境时一次性把依赖装齐用environment.yml更省事。写法大致是这样name: nlp39 channels: - conda-forge dependencies: - python3.9 - numpy1.24 - pandas - pip - pip: - some-pypi-only-package然后一条命令搞定conda env create -f environment.yml注意dependencies下面那个嵌套的pip:块它允许你在同一个文件里声明 pip 包conda 会先装完 conda 部分再调用 pip 装剩下的。这个设计正好对应前面说的先 conda 后 pip的顺序原则。3.5 安装过程中的常见卡点第一个卡点是卡在Solving environment很久。这是 conda 在做依赖求解包越多、约束越复杂耗时越长。conda 23.10 之后默认换成了 libmamba 求解器速度比老版本快一个数量级如果你的 conda 还慢得离谱可以考虑升级 conda 自身或者手动装conda-libmamba-solver。第二个卡点是下载中断导致CondaError: Downloaded bytes did not match Content-Length。这是包没下载完整重跑一次通常就好重跑还不行就换源或者清一下下载缓存。第三个卡点是 channel 优先级导致拿到了非预期的版本。同一版本号在不同 channel 都存在时conda 按 channels 列表顺序选。遇到我明明指定了版本怎么装的不是这个第一反应就该是查 channel 顺序。3.6 从已装环境反推安装清单有时候你不想手写 environment.yml而是想把一台机器上跑得好好的环境原样搬到另一台。这时候用导出conda env export environment.yml # 带 build 号最精确 conda env export --no-builds environment.yml # 去掉 build 号跨平台更友好 conda list --explicit spec-file.txt # 导出精确的包 URL--no-builds和默认的区别值得说一句带 build 号导出的文件在你当前平台上能一比一复现但换到别的操作系统或 Python 小版本上可能因为找不到那个精确的 build 而失败。跨平台搬环境时用--no-builds同平台复制用默认导出。4. 卸载包干净利落地移除卸载看着简单其实有两个容易翻车的点一是卸载的粒度选错二是没意识到它会连带移除一堆东西。4.1 conda remove 的几种粒度conda remove做的事不只是删掉那个包对应的文件它还会重新计算依赖图把那些只为这个包存在、现在没人依赖了的包一并清掉。这既是优点不留下垃圾依赖也是风险可能带走你还需要的包。conda remove -n myenv requests # 移除单个包 conda remove -n myenv requests pandas # 一次移除多个 conda remove -n myenv --all # 移除环境里所有包相当于删环境内容 conda env remove -n myenv # 直接删除整个环境最后两条的区别很多人搞不清。conda remove --all -n myenv是把环境里的包清空环境目录还留着conda env remove -n myenv是连目录一起删掉。日常想彻底清理用后者更干净。4.2 撤销与强制两个需要审慎使用的选项conda remove有一个很贴心的机制就是它默认会做一次事务回滚记录配合--dry-run你可以先看它到底要删哪些conda remove -n myenv requests --dry-run输出里会列出将被移除的包。如果发现某个你没打算删的包也在列表里原因是它依赖 requests或者 requests 依赖它而现在没人用了前者的话删不掉后者的话删了是对的。--force选项要理解清楚它的含义它跳过依赖检查直接删。这在环境已经被搞坏、需要强行清理时有用但正常情况不要用因为它可能留下一个依赖断裂的环境之后任何conda install都会因为环境不自洽而报错。4.3 卸载不干净怎么排查有时候conda remove报了成功但conda list里还能看到那个包或者import还能成功。可能的原因有两类一类是同名包被 pip 也装过一份。conda 删的是 conda 管的那份pip 装的那份还在site-packages里。这时候用pip uninstall 包名再删一次或者直接看pip list | grep 包名。另一类是包名本身不匹配。conda 里有些包的安装名和导入名不一致比如安装的是opencv导入的是cv2安装的是scikit-learn导入的是sklearn。卸载时要用安装名。排查的顺手命令是这两个conda list | grep 关键字 pip list | grep 关键字两边都查一遍基本能定位干净。4.4 别忘了清缓存conda remove只删环境里的文件不删包缓存。conda 会把下载过的包压缩文件和解压后的副本存在pkgs目录用久了能吃掉几十个 G。定期清理是必要的conda clean --all # 清所有缓存包缓存 索引缓存 下载残留 conda clean --packages # 只清没被任何环境引用的包 conda clean --index-cache # 只清索引缓存解决搜不到最新版本conda clean --index-cache这一条值得单独记住。当你确认某个包的新版本已经发布但conda search就是看不到时多半是本地索引缓存过期了清一下索引缓存再搜就有了。5. 升级包什么时候升、升谁、怎么回滚升级是四个动作里风险最高的一个。装包失败最多是没装上升级失败可能把一个原本能跑的环境弄坏而且是那种报错看起来跟升级毫无关系的坏法。所以升级的核心心法只有一句每次升级前先想好怎么退回去。5.1 update 和 upgrade 是同一件事先澄清一个常见困惑conda update和conda upgrade是同一个命令的两个名字行为完全一致没有区别。选哪个纯看个人习惯我一般写update因为和apt、pip的语感一致。真正需要区分的是升谁conda update conda # 只升级 conda 自身 conda update -n myenv numpy # 升级某个环境里的某个包 conda update -n myenv --all # 升级该环境里所有包 conda update -n myenv python3.11 # 升级 Python 到指定版本最后一条特别说明一下conda install python3.11和conda update python3.11在效果上很接近都会把 Python 换到 3.11 并连带重装所有依赖 Python ABI 的包。区别是install允许安装这个动作update强调更新实际场景里两个都能达到目的我一般用install因为它对这个版本当前没装的情况处理得更明确。5.2 单包升级还是整体升级--all看着爽但它会尝试把所有包升到最新牵一发动全身。我踩过的典型坑是跑了一次conda update --allnumpy 从 1.24 跳到 1.26某个依赖老版 API 的自研模块开始疯狂报警告虽然还能跑但输出里全是噪声。我的实际策略是日常不动--all需要什么升什么只升一个包时先--dry-run看它的连带影响确实要整体升级先在环境的克隆上做验证没问题再切过去第二条的--dry-run在升级场景比安装场景更有价值因为升级的连带范围通常更大。看输出时重点关注有没有downgrade升级操作里出现降级八成是依赖冲突在逼求解器做妥协。5.3 用 revision 做回滚这是 conda 的隐藏福利这是我觉得 conda 最被低估的功能。每一次改变环境的操作install、remove、updateconda 都会打一个快照叫做 revision。你可以列出所有快照然后一键回到任何一个conda list -n myenv --revisions输出是一串历史记录每条有编号和操作摘要比如2019-01-04 10:32:11 (rev 5)后面跟着它安装了什么。找到你要回到的那个版本号然后conda install -n myenv --revision 3一条命令回滚不用手动一个个降版本。这个机制的前提是中途没跑过conda clean --all把缓存清了因为回滚要重新从缓存里取文件。所以清理缓存前想一想最近有没有可能需要回滚的环境。5.4 升级踩坑实录第一个坑base 环境不要随便升级。base 里装着 conda 自身和它的运行依赖conda update --all在 base 里跑可能升到一个新版本 conda 不兼容的依赖组合结果就是 conda 命令本身都跑不动了。我就干过一次最后只能重装 Miniconda。升级 base 里的包之前先conda update conda把 conda 本身升到最新再动别的。第二个坑Python 小版本升级会重装很多东西。从 3.9 升到 3.11所有带 C 扩展的包numpy、pandas、scipy、lxml 等都要重新下载对应 ABI 的版本下载量不小中途断网就会留下一个半死不活的环境。这种升级建议在网络稳定的时段做或者在环境副本上做。第三个坑升级后import报错但包明明装上了。这通常是.pyc缓存或者 Jupyter kernel 没重启导致的。先关掉所有相关的 Python 进程和 Jupyter 内核再试一次还不行就查conda list里那个包的版本是不是真的换成新的了。6. 查看包信息把环境摸清楚排查问题的效率很大程度上取决于你有多快能摸清当前环境的状态。这几条查看命令看起来平平无奇但用熟了能省掉大量瞎猜。6.1 conda list 的花式用法conda list是使用频率最高的查看命令但它有几种变体很多人没用过conda list # 当前环境的全部包 conda list -n myenv # 指定环境 conda list numpy # 只看某个包 conda list --revisions # 环境变更历史 conda list --explicit # 输出精确 URL可复现 conda list --show-channel-urls # 带上包的来源 channel--show-channel-urls是我排查版本冲突时最爱用的一个。同一个包名一个来自 defaults 一个来自 conda-forge版本可能差好几个小版本不看来源根本想不通为什么行为不一样。6.2 conda info 与包详情conda info前面提过补一句它的--envs用法等价于conda env list输出的星号标出当前环境。多环境并存时这是确认我现在在哪的最快方式。想看某个包的详细元信息用conda search --infoconda search --info -c conda-forge numpy1.24.3它会打印这个包的依赖列表、构建信息、许可协议、文件名、大小等。看依赖列表是预判安装风险的有效手段如果依赖列表里出现了你的环境里已经装着的、且版本要求冲突的包那这次安装大概率会触发降级或失败。6.3 定位这个包到底是谁带来的环境用久了经常出现这种情况conda list里有一堆你没主动装过的包想去掉又怕删了别的东西也跟着坏。这时候的思路是找谁依赖它。conda 原生没有特别趁手的反向依赖查询实际做法有两种。一是用conda remove加--dry-run试删看它的输出里会不会连带移除其他包如果只移除它自己说明没别的包依赖它可以放心删。二是装一个conda-tree之类的辅助工具来看依赖树conda install -c conda-forge conda-tree conda tree leaves # 列出没被任何包依赖的叶子包 conda tree depends numpy # 查看某个包的下游依赖者conda tree leaves的输出很适合用来做环境瘦身那些叶子包如果不是你主动装的就是可以清理的候选。6.4 依赖冲突的定位套路遇到UnsatisfiableError这一类解法器报错不要慌着重试那个报错本身信息量很大。它通常会列出两到三个冲突项形如A 包要求 B 2.0但 C 包要求 B 2.0。定位步骤是把这几个包名记下来逐个用conda search --info 包名 -c 对应channel查它们各自支持的版本范围判断哪个包的约束是可以放松的通常是自己手动锁死版本的那个用--dry-run验证放松约束后是否能解有一个经验是环境中手动指定了精确版本的包越多求解失败的几率越高。因为每一条精确约束都在压缩解空间。所以除非有明确原因装包时尽量用宽松约束让求解器有腾挪空间。7. 常见问题速查表与避坑心得前面各章节散落了一些注意事项这里集中收一遍方便你在排查时直接对照。7.1 问题速查表现象大概率原因处理方式conda: command not foundPATH 未配置 / 未重开终端跑conda init后重开终端run conda init before conda activateshell 未注入 conda 函数conda init bash并重开终端Solving environment卡很久求解器慢 / 约束过复杂升级 conda 用 libmamba 求解器放宽版本约束UnsatisfiableError版本约束互相矛盾查冲突包的版本范围放松手动约束下载中断、字节数不匹配网络波动导致包不完整重跑一次或换源或conda clean --packages装上了但 import 不到激活的环境不对 / pip 与 conda 混装用conda info确认环境pip list查是否重复安装搜不到已知存在的新版本本地索引缓存过期conda clean --index-cache后重搜磁盘被吃满pkgs 缓存长期未清conda clean --all环境装坏想回退无 revision 记录或缓存已清平时别频繁clean --all坏了就重建环境7.2 我踩过的几个真实坑第一个坑是在 base 里做实验。刚上手那会儿图省事什么包都往 base 里装半年后 base 里有 300 多个包conda update --all跑到一半报冲突最后只能推倒重来。现在的做法是 base 只保留 conda 自身和极少数工具其他一律进独立环境。第二个坑是习惯性用--force。环境一旦报冲突就顺手加--force跳过检查看起来问题解决了实际上环境已经不自洽后面任何操作都会以一个更难懂的报错失败。正确做法是花时间看冲突到底出在哪两个包之间而不是绕过检查。第三个坑是不看 dry-run 直接装。前文提过那个 numpy 被降级的例子如果当时先 dry-run看一眼输出就能避免半小时的排查。现在我的肌肉记忆是任何涉及安装或升级的操作先加-d敲一遍。第四个坑是在共享服务器上往公共环境里装包。别人跑得好好的环境你装一个包顺带升级了 common 依赖第二天别人的脚本就报错了。多人共用的机器上一定用自己的环境或者用-n明确指定自己的环境名。7.3 一套可以直接照抄的日常习惯把上面的东西浓缩成一套我每天都在用的流程你可以直接拿去改开新项目conda create -n 项目名版本 python3.x建环境装依赖能 conda 就 conda先conda search确认装之前--dry-run看一眼补缺conda 里没有的用 pip 装装完不再回头动 conda锁版本环境调通后conda env export --no-builds environment.yml存档日常维护只升需要的包不做无脑--all动手前记下当前的conda list --revisions编号定期清理确认近期不需要回滚后conda clean --all收尾不用的环境直接conda env remove -n 环境名别留着占地方这套流程里最容易被忽略的是第 4 步的存档但它的价值恰恰最大。环境调通那一刻是最容易复现的过了两周再想重建同样的一组版本光靠记忆基本做不到。8. 关于 conda 和 pip 分工的一点个人体会一路写下来如果你只记一件事我希望是这句conda 是一个跨语言环境管理器pip 是 Python 包安装器两者不是替代关系而是分工关系。真正让环境变得不可维护的从来不是 conda 慢或者 pip 快而是两个工具在同一批包上反复覆盖对方的成果。我自己的习惯是在每个环境的根目录放一个environment.yml它就是这个环境的事实来源。任何时候环境跑不通了我都先看这个文件里记的是什么版本再和conda list的实际输出对一遍差异通常就指向问题所在。这个动作比盲目的重装环境有效得多也比升级所有包后再回头找凶手省时间。至于开头提到的那些升级包、卸载包的操作敲熟了都是几秒钟的事真正花时间的永远是判断该不该动。多花三十秒跑一次--dry-run往往能省下半小时的排查。
返回列表