"python -m pip install <package-name>" 和 "pip install <package-name>" 之间的区别是什么?"

3
我遇到了一个问题,我尝试在虚拟环境中运行Django应用程序,但它一直显示缺少需要安装的软件包的错误,尽管我之前已经使用pip install <package-name>进行了安装。
直到我使用python -m pip install <package-name>安装了缺失的软件包,问题才得以解决。
我的问题是这两个命令有什么区别?其中一个命令会将软件包安装到虚拟环境中,另一个则全局安装吗?我很困惑。
另外注意:当运行pip freeze时,显示的已安装软件包与运行python -m pip freeze时显示的不同。

更新1:

运行pip list -v显示使用pip install <package-name>安装的软件包位于虚拟环境文件夹下的lib/python3.8/site-packages目录中,而运行python -m pip list -v则显示使用python -m pip <package-name>安装的软件包位于/usr/lib/<python3或python3.8>/<site-packages或dist-packages>目录下。


更新2:

我在运行Ubuntu 20.04 64位的VPS上经历了以上所有问题。奇怪的是,当我在运行Linux Mint的本地机器上执行这两个命令时,两个命令执行的结果完全相同,在我的本地机器上pip install <package-name>python -m pip install <package-name>之间没有任何区别。


更新三:

我所做的是删除了我的虚拟环境,然后在同一目录下创建了一个同名的新环境。为了更清晰地表述,以下截图展示了我遇到的问题:

1- 当我在全局范围内运行pip list -v时,下面的截图显示了结果: enter image description here

2- 当我在全局范围内运行python -m pip list -v时,下面的截图显示了结果: enter image description here

以上两个截图显示的是相同的结果(两个命令都在全局范围内执行,没有活动的虚拟环境)。

3- 当我在我的虚拟环境中运行pip list -v时,下面的截图显示了结果:

enter image description here

4- 下面的截图显示了我在虚拟环境中运行python -m pip list -v时的结果:

enter image description here

上述两个截图显示了不同的结果(在虚拟环境激活时执行两个命令)。
尽管我创建了一个新的虚拟环境,但问题仍然存在。

@AlejandroBlasco 运行 pip --version 命令返回以下结果: pip 20.0.2 from /root/<project-name>/my-virtual-env/lib/python3.8/site-packages/pip (python 3.8), 而运行 python -m pip --version 命令则返回以下结果: pip 20.0.2 from /usr/lib/python3/dist-packages/pip (python 3.8) - Osama Radwan
问题似乎已经变成了完全不同的一个。 - mousetail
如果你在虚拟环境外和内使用 which python 命令,它们返回的路径是否相同?如果是,则说明 activate 没有更新路径,你需要手动运行 hash -r 命令来更新虚拟环境。 - Javier Buzzi
pip怎么样?你能展示一下路径吗? - Javier Buzzi
@JavierBuzzi pip在两个命令中显示了相同的路径。 - Osama Radwan
显示剩余6条评论
3个回答

3

欢迎来到符号链接、PATH的二进制可发现性和shebang #!/...(*nix)的世界。

在深入了解所有内容之前,让我们先谈谈pip是什么:

pip既是Python模块(即python -m pip-m表示模块)),也是一个二进制文件$ pip。作为程序的二进制文件pip是可选的,它不是必需的。但是,位于安装所有Python模块的site-packages中的模块pip则是绝对必需的!如果没有它,一切都将无法正常工作,或者更糟糕的是,您可能会使用来自您已安装的另一个Python版本的pip模块,然后您真的会头破血流……

PATH变量

echo $PATH(*nix)将显示类似以下内容:

$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin

那么系统会通过终端首先在/usr/local/bin,然后是/usr/bin等目录中查找二进制文件。如果它在第一个查找的地方找到了,那就很好,不需要继续搜索了。你可以在不同的目录中拥有相同的二进制文件名称,但PATH的顺序才是关键,第二个将永远不会被执行。

我为什么要提到这个?假设你安装了多个版本的pythonpip,并且把它们安装在错误的位置?路径顺序错了怎么办?那个二进制文件将无法按预期运行。

符号链接

你可以拥有不同版本的pippython,它们彼此独立,这意味着即使你使用的是版本为3.8的python,也不意味着你的pip指向的是相同的python二进制文件。

一个快速检查的方法是:(适用于pip

$ ls -ls $(which python)
0 lrwxr-xr-x  1 root  wheel  75 Jan  1  2020 /usr/bin/python -> ../../System/Library/Frameworks/Python.framework/Versions/2.7/bin/python2.7

您可以看到它正在使用系统版本的python,如果我只运行python,则会忽略任何其他版本,我可以通过指定版本号来“修复”这个问题,例如python3.8...

您可以通过首先找出可用的二进制文件来解决此问题:

$ echo $PATH | sed 's/:/ /g' | xargs -n1 -I{} find {} -name 'python*' | grep -v 'config$'
/usr/local/bin/python3
/usr/local/bin/python3.9
/usr/local/bin/python3.8
/usr/bin/python3
/usr/bin/python
/usr/bin/python2
/usr/bin/pythonw
/usr/bin/pythonw2.7
/usr/bin/python2.7

假设我想将默认的python设置为python3.8,那么可以执行以下操作:

$ ln -s /usr/local/bin/python3.8 /usr/local/bin/python
$ hash -r

hash -r 告诉终端忘记之前找到的所有二进制文件,否则你的新符号链接将无法工作。

问:为什么不直接覆盖它呢?

你们中聪明的人可能会问,如果默认的 python/usr/bin/python 中,为什么不直接使用覆盖呢?

$ ln -sf /usr/local/bin/python3.8 /usr/bin/python

/usr/bin/是受保护的,而且乱搞系统偏好设置是很不好的形式。最好不要碰它,/usr/local/bin是属于你(机器的用户)的。

Shebang #!/...

大多数情况下,你会发现pip二进制文件(顺便说一下,它只是纯文本)像这样启动:

#!/usr/bin/env python

或者

#!/usr/local/bin/python3.9

这段文字的意思是,我是一个文本文件,需要使用以下可执行文件来运行。在第一个示例中,它使用系统来查找二进制文件python,就像您在终端中查找一样,请参见上面的PATH变量。第二个示例是系统使用的二进制文件的直接路径。
请记住,pip是一个模块,二进制文件pip实际上是这样说的:(稍作修改,使其尽可能基础,并说明一个观点)
#!/usr/bin/env python
import sys
from pip._internal.cli.main import main
sys.exit(main())
  1. 使用 python 二进制文件
  2. 导入 sys 模块以便稍后使用...
  3. pip 模块导入 main 函数
  4. 运行 main,无论它返回什么代码,请将其传递给 sys.exit(..),通常会返回 0,但这不重要...只是一个 FYI

好的,我非常感谢你的努力。我理解了你的回答。但是我没有找到你的回答解决我的问题的地方。 - Osama Radwan

1
通常情况下没有区别。 python -m 标志允许您运行特定的模块。例如,您可以运行 python -m http.server 在当前目录中启动一个简单的服务器。
Python 包括一个 "scripts" 目录。根据您的系统设置,来自 scripts 目录的模块可以直接从 shell 运行,无需使用 python -m 前缀。例如,通常您会直接从 shell 运行 black
但是,您可能不想信任直接从 shell 运行 Python 模块的各种原因,包括:
  • 运行特定的 Python 版本。您可以执行 py -3.6 -m pip install something 来指定 Python 版本
  • 出于某些原因,您不想将脚本文件夹添加到 PATH 中。也许是因为某些名称与计算机上的其他程序冲突。
在虚拟环境中,两个命令通常会执行相同的操作,因为 virtualenv 将正确设置您的 PATH。

但是,为什么每个命令在不同的目录中安装软件包呢?它们不应该将软件包安装到相同的位置吗? - Osama Radwan
如果两者使用相同的Python安装(相同版本,相同的虚拟环境),它们将不会安装在不同的位置。如果它们不同,则会安装在不同的位置。 - mousetail
有时候在更新/修改Python现有的虚拟环境时,会出现无法正常工作的问题。如果您在使用virtualenv时遇到问题,请尝试删除该环境并重新创建。 - mousetail
好的,我会尝试。 - Osama Radwan

0

当你需要定义你的软件包安装到哪个Python版本时,这非常方便(对我的糟糕英语表示抱歉)。 比如,你同时有Python3.8和Python3.10两个版本。 你可以执行python3.8 -m pip install djangopython3.10 -m pip install django


在我的情况下,这两个命令都使用相同的 Python 版本(python3.8)。 - Osama Radwan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接