# 聊聊Python生态里那个被用烂了的工具:pip
前些天跟一个刚入行的朋友聊天,他说自己装了Python之后,第一步就是装了个Anaconda,因为“网上都这么说”。我问为什么不用pip,他愣了一下说“那个不是装库的吗?”这话乍一听没错,但仔细琢磨,就好像说“冰箱不是用来保鲜的吗”一样——对,但只说对了一小半。
pip是什么
pip的全称是“Pip Installs Packages”,一个递归缩写,很有点程序员式的幽默。它是Python官方推荐的包管理工具,从Python 3.4开始就随解释器一起打包。本质上它就是个命令行程序,专门负责从PyPI(Python Package Index,全球最大的Python包仓库)下载和安装第三方库。
有意思的是,很多人天天用pip install,却不知道它到底在干什么。其实整个过程可以简化成三步:解析依赖关系、下载wheel或源码包、解压并放置到site-packages目录。如果你是源码安装,还要负责编译。这些东西对日常使用者来说都是透明的,但这种“透明”有时候也会带来麻烦,后面再聊。
它到底能做什么
如果只回答“安装包”,那就像说手机只能打电话。pip能做到的事情远比这个多:
安装单个包当然是最基本的。但如果你在团队协作中,会发现pip结合requirements.txt文件的价值。所谓requirements.txt就是一个文本文件,列着你项目依赖的所有包及其版本。团队里的新人拉下代码后,一条pip install -r requirements.txt就能复刻出一模一样的开发环境。这个东西我在无数个项目里见过,但大部分团队都写成了“xx>=1.0.0”这种宽泛的版本——说实话,这种写法跟没写区别不大,后面再说。
pip还能帮你管理已经安装的包。pip list列出所有包,pip show查看某个包的详细信息,包括它依赖了什么、安装在了哪里。开发中遇到“奇怪报错”时,我经常先跑pip list看一眼,往往能找到问题根源,比如某个库的版本跟其他库冲突了。
还有一个不太被人注意的功能是pip download。它只下载包但不安装,这在离线环境或者需要手动审查包代码时非常有用。以前做过一些安全检查严格的客户项目,所有的依赖包都要求先下载下来人工过一遍,再拿到内网安装,这个功能帮了不少忙。
怎么用才顺手
安装包最常见的是pip install requests,但大部分人不知道可以加上--no-cache-dir这个参数。有时候你遇到“明明装了新版却还是旧版行为”的问题,很可能是因为pip从缓存中拿了个旧版本。加上--no-cache-dir强制全量下载能解决很多莫名其妙的问题。
升级包是pip install --upgrade,但pip install -U是简写,一样。卸载是pip uninstall。这里有件趣事,见过有人写了个脚本反复装包卸载包,测试环境搞得很乱——其实可以用pip freeze > requirements.txt备份当前环境状态,出了问题直接用pip install -r requirements.txt --force-reinstall一键还原。
对于虚拟环境的支持,pip配合venv模块最常用。创建环境python -m venv myenv激活后,pip就装在这个环境里了,互不干扰。很多人不知道python -m pip和直接pip的区别——前者保证你用当前环境对应的解释器,后者可能有路径冲突。我习惯写python -m pip,少很多“明明装了却说没有”的问题。
真正可能在实战中帮到你的东西
版本锁定是个常见陷阱。写requirements.txt的时候,最好用==1.0.0这种固定版本,而不是>=1.0.0。如果你说“我是为了兼容性”,那更应该锁死——因为你不知道上游库的新版本会引入什么破坏性变更。实际经历:有个微服务因为某个库从1.2.3升到1.2.4,内部改了某个函数的默认值,导致线上数据异常。从那以后,但凡生产项目,我都会在requirements.txt里锁死版本号。
另一个容易被忽视的是--find-links参数。它允许你指定一个本地目录或私有仓库作为包的来源。在大公司工作过就知道,很多情况不能直接连外网。可以在公司内部建个PyPI镜像,或者索性把包都整理到一个共享文件夹,用pip install --find-links /shared/packages requests从本地安装。速度比从仓库下载还快。
还有个小技巧:pip config可以改写配置文件。比如设置超时时间或者默认使用镜像源,可以写一行pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple,以后就不用每次输-i参数了。
换几个别的看看
聊pip肯定绕不开conda。conda是Anaconda和Miniconda自带的包管理器,它不仅能管Python包,还能管系统级的二进制依赖,比如CUDA、OpenCV这类。如果你做数据分析、机器学习,conda确实省心,因为很多数学库在pip上装可能会遇到编译问题,conda作者已经帮你编译好了。
但conda有两个问题:一是仓库不如PyPI全,一些较新或者不那么主流的包只有PyPI有;二是它的环境管理相对独立,跟系统pip混用容易出问题。我一般建议普通Web开发或者脚本项目用pip+venv,而处理数据科学相关时可以考虑conda。
还有poetry和pipenv。poetry的出现是解决依赖管理更精细化的问题,它会检查整个依赖树确保没有冲突。但说实话,对于大多数中小型项目来说,足够仔细地写requirements.txt加上定期更新,效果没有本质差别。pipenv把包管理和虚拟环境合了,但个人使用体验里感觉有点过重,而且锁文件机制在一些场景下反而增加了复杂度。
用pip这么多年,它最让我欣赏的地方就是简单。简单到几乎没有学习成本,简单到习惯了就忘了它的存在。无论后来出了多少花哨的工具,pip始终是自己最常碰到的选择——可能不是最好的,但够用,而且可靠。