使用 `python -m pip` 而非仅使用 `pip` 会有什么影响?

200
当我使用python -m pip install <package>时,与仅使用pip install <package>有何不同?同样,为什么我要写python -m pip install --upgrade pip来升级Pip,而不是直接使用pip install --upgrade pip呢?

9
这是如何将一个模块作为脚本运行的方法。 - jedwards
29
@Jean-FrançoisFabre,我认为这并不是关闭问题的好理由。很多得到高票数的基础问题在文档或手册中都很容易找到答案。无论如何,我的观点是0。 - Chris_Rands
2
可能是What is the purpose of the -m switch?的重复问题。 - phd
3
不同意关闭。关闭会削弱问题的意义,这是一个好问题。不幸的是,答案并不那么好。 - KansaiRobot
29
“阅读手册”这样的回答缺乏同理心。对于那些知道这是一个基本问题,可以在手册中找到答案的人来说,这听起来很容易 - 问题在于,当你在学习时,你无法区分应该很容易找到的东西和复杂的东西之间的区别。 - Hector Ordonez
显示剩余6条评论
8个回答

126
考虑以下情形。
您安装了三个版本的Python:
- Python 3.7 - Python 3.8 - Python 3.9
您的“默认”版本是3.8。它是出现在您的路径中的第一个版本。因此,当您在shell中键入`python3`(Linux或Mac)或`python`(Windows)时,您将启动一个3.8解释器,因为这是在遍历路径时找到的第一个Python可执行文件。
假设您要启动一个新项目,并想使用Python 3.9。您创建了一个名为`.venv`的虚拟环境并激活它。
python3.9 -m venv .venv         # "py -3.9" on Windows

source .venv/bin/activate    # ".venv\Scripts\activate" on Windows 

我们现在已经使用Python 3.9激活了虚拟环境。在shell中输入python即可启动3.9解释器。 但是,如果您键入
pip install <some-package>

那么使用的是哪个版本的pip呢?是默认版本Python 3.8中的pip,还是虚拟环境中的Python版本?

解决这种歧义的简单方法就是使用:

python -m pip install <some-package>
-m 标志确保您正在使用与活动Python可执行文件绑定的pip。
即使您只安装了一个全局版本的Python并从中创建虚拟环境,始终使用-m 是一个好习惯。
关于所谓的路径,它是一个目录列表,其中系统搜索可执行文件。当您输入命令(例如python)时,该列表从第一个目录遍历到最后一个目录,搜索与您输入的命令匹配的文件名。
如果找到文件名/命令,则匹配的文件将在不考虑潜在的后续匹配的情况下执行。如果没有匹配项,则会出现“找不到命令”或其变体。这种行为是有意设计的。
在UNIX系统上,路径环境变量称为$PATH,而在Windows系统上,则称为%PATH%
关于-m标志的更一般性评论(2022年12月)
大多数人查看此内容时可能希望使用pip提供的解释。然而,在更一般的情况下,当使用python -m some_module时,-m标志使Python将some_module作为脚本执行。这在文档中有说明,但可能需要一些基本知识才能理解。
什么是“作为脚本运行”? 在Python中,模块some_module通常通过在导入文件的顶部使用import some_module语句导入到另一个Python文件中。这使得可以在导入文件中使用在some_module中定义的函数、类和变量。 要将some_module作为脚本而不是导入它,您需要在文件内部定义一个if __name__ == "__main__"块。当在命令行上运行python some_module.py时,将执行此块。这很有用,因为您不希望在导入其他文件时运行此代码块,但您希望在从命令行调用时运行此代码块。
对于项目内部的模块,此脚本/模块结构应该保持不变,因为当从终端运行时,Python会从您的工作目录中找到模块。
python some_module.py

但是对于Python标准库中的模块,这种方法行不通。Python文档中的示例使用了timeitpip也适用):

python3 timeit -s 'print("hello")'  # 'python timeit.py ...' fails as well 

这会返回错误:"python: can't open file '/home/<username>/timeit': [Errno 2] No such file or directory"

添加-m标志告诉Python在路径中查找timeit.py并执行文件中的if __name__ == "__main__"子句。

python3 -m timeit -s 'print("hello")'

这个按预期工作。

if __name__ == "__main__" 代码块的源代码可以在 这里 找到,它是 timeit 模块的一部分。


venv 的文档中得知:"当在虚拟环境中使用时,常见的安装工具(如 pip)将会自动将 Python 包安装到虚拟环境中,无需显式地告知。" - updogliu

46

来自Python文档:

由于参数是模块名,因此不应给出文件扩展名(.py),module-name应该是有效的Python模块名称,但实现可能并不总是强制执行此规定(例如,它可能允许您使用包括连字符的名称)。

还允许使用软件包名称。 当提供软件包名称而不是普通模块时,解释器将执行<pkg>.__main__作为主模块。 这种行为故意与作为脚本参数传递给解释器的目录和zipfile的处理方式类似。


5
所以这只是像普通可执行文件一样运行Python脚本?我不明白什么时候需要使用“-m”标志。我的脚本在没有该标志的情况下运行时总是有效的。 - Charlie Parker
3
我不确定在这种情况下模块和脚本的区别,但我发现评论很有意思:“这是你如何将模块作为脚本运行的。” 尽管主要问题仍然是什么是脚本和模块,以及为什么真正需要“-m”。 - Charlie Parker

22

如果您输入python --help

您将得到:

// More flags above
-m mod : run library module as a script (terminates option list)
// and more flags below

如果您使用command --helpman command,终端中的许多内容都会向您展示如何使用它。


110
主要问题仍未得到回答,"to run as a script" 是什么意思。 - Serge Mosin
1
两年半过去了,仍然没有人打算告诉我们......Python的导入是一场噩梦,文档描述完全没用(“作为模块运行”??)。我怀疑这些人中没有一个人知道这个标志的作用。也许现在已经没有人记得了。 - markemus
1
通过调用 python -m pip [flags],您不必提供 pip.py 存在的目录路径或将其作为当前工作目录。相反,Python 本身通过 PYTHONPATH 导入 pip 模块并运行它,这也意味着当加载和执行 pip.py 或任何其他“作为模块”运行的脚本时,它们都不会具有 __name__ == '__main__' - nigh_anxiety

20

-m表示模块名称

来自命令行和环境:

python [-bBdEhiIOqsSuvVWx?] [-c command | -m module-name | script | - ] [args]


19
当在命令行中使用 -mpython 语句,并跟上一个 <module_name> 时,可以将该模块作为可执行文件运行。
您可以参考Python文档或运行 python --help 获取更多信息。

15
所以这只是像普通可执行文件一样运行Python脚本吗?我不明白何时需要使用-m标志。当我没有使用它时,我的脚本总是可以正常运行的。 - Charlie Parker
6
据我所了解,-m 标志是非常有用的,因为它将在 sys.path 中所有可用的路径中搜索模块/包,而不仅仅是当前工作目录。 - qwerty_url
@qwerty_url 但这不是通常发生的吗? - Charlie Parker
2
@CharlieParker 如果你在终端中尝试 python timeit,并且当前工作目录中没有名为 timeit 的文件/文件夹,则会出现“没有这样的文件或目录”错误。但是,如果你添加 -m 标志,那么 Python 将运行标准库中附带的 timeit 包。 - qwerty_url

8

这实际上是一个有趣的问题,让我们来探索由@jedwards在评论中提到的pep 338。

-m标志最初用于将模块名称转换为脚本名称。 在Python 2.4中,其行为是:

the command line is effectively reinterpreted from python <options> -m
<module> <args> to python <options> <filename> <args>.

这个功能看起来并不是很有用,但这正是它以前的作用。Pep 338进一步扩展了这个行为。

所提议的语义相当简单:如果使用 -m 执行一个模块,则使用 PEP 302 导入机制来定位模块并检索其已编译的代码,然后根据顶级模块的语义来执行该模块。

它进一步解释说Python将识别模块所在的包,使用标准过程导入该包,并运行该模块。从我的理解来看,“python3 -m package.module”与调用以下命令相同:

python3
from package import module

-m标志将以__file__而不是__main__的方式运行该模块。它还会将本地目录插入sys.path中,而不是脚本所在的目录。这将破坏相对导入的功能,虽然这并非意図之中,因此建议始终使用绝对导入。此外,它还取决于您的调用方式——“python3 -m package.module”与“python3 -m module”不同。

理论上很简单——它加载Python并导入模块,而不是将代码转储到__main__中。 实际上效果很多。 它是一个行为不同的不同导入系统。 其中有些变化并非有意,并且仅在实施后才被发现。 Python的导入机制比较混乱,如果感到困惑是正常的。


你有没有想过为什么-m需要点符号表示法?我使用它来复制ipython命令“run package/module.py”的效果,以便更好地管理内存。点符号表示法是这个工作流程的一个主要缺点。 - nedlrichards
1
@nedlrichards 它与脚本内的导入语法匹配,我相信它将允许您正确加载依赖于__init__进行构建的子模块。 - markemus

5

有些人说这个问题太简单了,不值得回答,但实际上这是一个很难回答的问题。

据我所知,它的实际用途是让你在不在目录中的情况下使用点符号来运行你的脚本。

你可以运行 python -m path.to.my.happy.place

而不必在 path/to/my/happy 目录中运行 python place.py


为什么要使用点表示法?它很混乱,而且在命令行上无法自动完成。 - nedlrichards
1
@nedlrichards 一个例子是我遇到的情况,我正在运行的.py文件在一个文件夹中,而我制作的自定义库则在上一级并且在另一个文件夹中。我不断遇到错误,例如“尝试使用未知父包进行相对导入”。为了解决这个问题,我必须使用-m标志,并在终端中使用点符号运行我的.py文件。同样的方法也用于在AWS Lambda上运行,它可以正常工作。 - Kush

2
如果您安装了多个Python版本并且想要升级pip pip install --upgrade pip,那么您如何知道哪个Python版本会受到影响?这取决于shell的路径变量。在这种情况下,您可能还会收到警告。为避免混淆,请使用-m,然后它会查找sys.path变量。这是-m的另一个优点。
# importing module
import sys
  
# printing all directories for 
# interpreter to search
sys.path

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