你在写 Python 项目的时候是不是常常会使用 pip install 来安装相关的依赖,但这其实到后面会产生一些不必要的麻烦,最近看到了篇《Why you should use python -m pip,作者非常苦口婆心的劝人们一定要使用 python -m pip 而不是直接 pip,我觉得写得挺好的,随即给翻译了一波,希望对你有帮助。

什么是 python -m pip?

首先,当你执行 python -m pip 的时候,是在指定 Python 解释器来执行 pip。

比如当你这么执行:

/usr/bin/python3.7 -m pip

就是在指定在 /usr/bin/python3.7 下的解释器来执行 pip。

为啥要使用 python -m pip 而不是直接 pip/pip3 呢?

你可能会说,“行了行了,我都知道,但我就是想直接通过 pip 命令来执行 pip,你吹啊?”

答案是:“可以啊,不过呢,但你这么做的话,你到时候会很乱很麻烦”。

我来给你举个例子。

比如说我的电脑现在安装了两个 Python 的版本(可能还更多),分别是 Python3.7 和 3.8,那么现在,我直接在终端执行 pip 去 install,请问它会为哪个版本的 Python 解释器进行安装?

我想你可能会一脸懵逼,因为没有更多的细节,所以你不知道。

首先你要知道我的环境变量 PATH 设置的是什么,比如 /usr/bin 和 /usr/local/bin,谁在前谁在后。

这时候你可能会说,我只要记住我在不同的目录分别安装了 Python3.7 和 Python3.8,我就会知道 PATH 上哪个 Python 版本在前面。

但假设你手动安装了两个 Python 版本,也许你的系统本身带有了 Python3.7.3 版本,然后你还安装了 Python3.7.5,这时候两个版本都安装在了 /usr/local/bin 下,那么你现在能知道 pip 和 哪个 Python 解释器相关么?

答案就是你依然不知道,除非你知道什么时候安装了什么 Python 版本,并且知道最后一次写入 /usr/local/bin/pip 的是哪个,否则你压根不会知道执行 pip 的时候用的是哪个版本的解释器。

你又要说了,那我总是安装最新的版本不就好了,比如我安装最新的 Python3.8.0,它比 3.7.5 要新。

好吧,但是当 Python3.7.6 版本出来的时候会怎么样呢?你的 pip 命令会将使用 Python3.8 转化为 Python3.7。

而当你使用 python -m pip ,上面所有的歧义立马消失,比如说我使用 python3.8 -m pip,那么我肯定知道 pip 用的是 Python3.8 解释器来安装使用。

如果你是 Windows 用户,你使用 python -m pip 还有一个好处,那就是可以让 pip 自我更新。

因为当你直接执行 pip install –upgrade pip 的时候,Windows 系统不会让你覆盖 pip.exe, 但你在前面加个 python -m,你就可以避免这个问题,因为这时候运行的是 python.exe,而不是 pip.exe。

那我在激活环境应该怎么搞?

通常当我向一群人解释这一点时,难免会有人说,“我都是在虚拟环境中操作的,所以你说的这一点根本不适合我”。

总是使用虚拟环境这点做得不错,但老实说,即使在完全没有必要的情况下,我仍然主张使用 python -m pip

首先,如果你使用的是 Windows 系统,你还是需要使用 python -m pip,这样你才可以在你当前环境中更新它。

其次,即使你使用的是其他系统,我还是会说应该使用 python -m pip,因为它在任何情况都能工作。不仅仅是在你忘记激活虚拟环境时防止发生错误,而且还意味着任何看到你这么操作的人都能学到这一点。就我个人来说,我不认为你少打几个字就能在最佳的实践中走捷径。它还可以防止你在忘记激活虚拟环境的时候,写一下自动化脚本而发生错误。

就说我自己吧,我使用任何依赖于解释器的工具,我都会使用 -m,不管是不是激活了环境,都能非常有目的且清晰的说明我希望使用什么 Python 解释器。

不要啥都安装到你的全局解释器里面

虽然我们在谈论如何避免弄乱你的 Python 安装,但我还要指出一点:当你在本地开发的时候,永远不要把东西安装到你的全局 Python 解释器里面去。如果它是你系统安装的 Python,那么你安装了一个操作系统所依赖的不兼容版本的库,可能会破坏你的系统。

即使你安装了自己的 Python 版本,我仍然强烈建议你不要在本地开发的时候直接都把包安装进去。因为你最终会在你的项目之间混合各种可能互相冲突的包,你对每个项目真正的依赖都会很模糊,等等。

所以使用环境将你的个人项目和工具互相隔离开来要好很多。在 Python 社区中有两种环境的类型:虚拟环境和 conda 环境。

甚至还有一种方法可以以隔离的方式来安装 Python 工具。

如果你需要安装工具

为了让 Python 工具得到隔离,我会使用 pipx 来安装,因为每个安装的工具都会拥有自己的虚拟环境,所以它们不会互相冲突。如果你想单独安装 Black 模块,你可以直接安装,不会意外破坏你安装的 mypy 模块。

如果你的项目需要一个环境(没有使用 conda 的情况下)

当你需要为项目创建虚拟环境时,我个人总会使用 venv 和虚拟环境。它包含在 Python 的标准库中,所以它始终可以通过 python -m venv 获取的。

一丢丢小历史:我实际上删除了用来创建虚拟环境的 pyvenv 命令,这正是你应该使用 python -m pip 而不是 pip 的原因;仅从命令看,你无法知道 pyvenv 命令为哪个解释器创建了一个虚拟环境;记住,你可以不用激活环境来使用其中的解释器;执行 .venv/bin/python 和你激活了环境并输入 python 一样有效。

现在有人仍然喜欢 virtualenv,因为它在 Python2 上可用,并且有一些其他的额外功能。就我个人而言,我不需要这些额外的功能,并且拥有 venv 集成,就意味着我不需要使用 pipx 在每台机器安装 virtualenv。但是如果你创建虚拟环境还需要 venv 一些不能够满足的需求,那么你可以看看 virtualenv 是否满足你的需求。

如果你是 conda 用户

如果你是 conda 用户,那么你可以使用 conda environments 来达到与 venv 所提供的虚拟环境同样的效果。我不打算谈论你是否应该针对你的请款使用 conda 而不是 venv。但如果你在使用 conda 时,你应该知道你可以为你的项目创建 conda 环境而不是将所有的东西都安装在你的基础环境中,并且能够清楚的知道你的项目依赖了什么。(这也是为什么说要使用 miniconda 而不是 anaconda,因为前者大小还没有后者的十分之一。)

容器

在容器中工作是另一种选择,因为此时你可以跳过环境,因为整个「机器」就是环境,只要你没有将 Python 安装到容器系统中,你就可以自由地进行全局暗转个,以保持容器的简单清晰。

重复一下我的观点,真的想让大家明白这一点

不要安装到全局的 Python 解释器中!在本地开发的时候,一定要使用一个环境!

我无法计算我有多少次不得不帮助那些 pip 安装错地方的人。还有那些破坏自己系统,或者想知道为什么他们安装东西会产生冲突等等,因为他们呢懒得在本地机器上设置环境。

所以,请理智一点,使用 python -m pip,并始终使用虚拟环境。