-m开关的目的是什么?

Cha*_*net 147 python

你能告诉我调用之间的区别吗?

python -m mymod1 mymod2.py args
Run Code Online (Sandbox Code Playgroud)

python mymod1.py mymod2.py args
Run Code Online (Sandbox Code Playgroud)

看来在这两种情况下mymod1.py被调用,sys.argv

['mymod1.py', 'mymod2.py', 'args']
Run Code Online (Sandbox Code Playgroud)

那么-m切换是什么?

agf*_*agf 125

PEP 338Rationale部分的第一行说:

Python 2.4添加了命令行开关-m,允许使用Python模块命名空间定位模块,以便作为脚本执行.激励性的例子是标准的库模块,例如pdb和profile,而Python 2.4的实现对于这个有限的目的来说很好.

因此,您可以通过这种方式在Python的搜索路径中指定任何模块,而不仅仅是当前目录中的文件.你是正确的,python mymod1.py mymod2.py args具有完全相同的效果.该Scope of this proposal部分的第一行说明:

在Python 2.4中,使用-m定位的模块就像在命令行上提供了它的文件名一样.

-m更多可能,比如使用作为包的一部分的模块等.这就是PEP 338的其余部分.阅读它以获取更多信息.

  • 我最喜欢的`-m`用法是`python -m SimpleHTTPServer`.当我需要在不使用USB闪存驱动器的情况下共享某些文件时非常方便. (39认同)
  • @arifwn运行Python3需要稍微更新为`python -m http.server`,这仍然很棒! (16认同)
  • TL; DR:1)您可以运行`python -m package.subpackage.module`并使用正常的解析机制,您不必指出确切的`.py`文件.2)可以从运行的模块进行相对导入,无需任何解决方法,因为它的包将在整个过程中加载.3)绝对导入将基于您当前的目录,而不是`.py`文件所在的目录(`''`位于`sys.path`的头部,而不是`/ path/to/my`,如果脚本位于`/ path/to/my/script.py`). (12认同)
  • 这个答案没有明确说明的是**这仅适用于可执行模块的子集**,即具有“__main__.py”文件。大多数不会并且会破坏,例如“python -m sys 'print(sys.version)'”失败并显示“python: No code object available for sys”。建议你在答案中说清楚。 (2认同)

Mar*_*ker 76

尽管这个问题已经被多次询问和回答(例如,这里这里这里这里),但在我看来,没有现有的答案完全或简洁地捕捉到-m标志的所有含义。因此,以下内容将尝试改进之前的内容。

简介 (TLDR)

-m标志做了很多事情,但并不是所有这些将需要所有的时间。简而言之,它可用于:(1) 通过模块名而不是文件名从命令行执行 python 代码 (2) 添加一个目录以sys.path用于import解析和 (3) 执行包含来自命令行的相对导入的 python 代码。

预赛

为了解释-m标志,我们首先需要解释一些术语。

Python 的主要组织单位称为模块。模块有两种风格之一:代码模块和包模块。代码模块是包含 python 可执行代码的任何文件。包模块是包含其他模块(代码模块或包模块)的目录。最常见的代码模块类型是*.py文件,而最常见的包模块类型是包含__init__.py文件的目录。

Python 允许以两种不同的方式唯一标识模块:模块名和文件名。通常,模块由 Python 代码中的模块import <modulename>名(例如)和命令行中的文件名(例如)标识python <filename>。所有 python 解释器都能够通过遵循相同的几个明确定义的规则将模块名转换为文件名。这些规则取决于sys.path变量。通过改变这个变量,你可以改变 Python 将模块名解析为文件名的方式(有关如何做到这一点的更多信息,请参阅PEP 302)。

所有模块(代码和包)都可以执行(即,与模块关联的代码将由 Python 解释器评估)。根据执行方法(和模块类型),评估哪些代码以及何时评估可能会发生很大变化。例如,如果执行一个包模块 via python <filename>then<filename>/__main__.py将被执行。另一方面,如果一个人通过执行相同的包模块,import <modulename>那么只有包的__init__.py将被执行。

历史发展 -m

-m标志首先在Python 2.4.1 中引入。最初它的唯一目的是提供一种替代方法来识别要从命令行执行的 python 模块。也就是说,如果我们知道模块的<filename><modulename>,那么以下两个命令是等效的:python <filename> <args>and python -m <modulename> <args>。根据PEP 338,此迭代的一个限制是-m仅适用于顶级模块名称(即,可以直接在sys.path没有任何中间包模块的情况下找到的模块)。

随着PEP 338的完成,该-m功能被扩展为支持<modulename>超越顶级的表示。这意味着http.server现在完全支持这样的名称。这个扩展还意味着__init__.py除了 modulename 本身引用的模块之外,现在评估 modulename 中的每个父包(即评估所有父包文件)。

对于最后的主要特征增强-m带着PEP 366。通过此升级-m,不仅可以支持绝对导入,还可以在执行模块时支持显式相对导入。这是通过更改来实现的,-m以便将__package__变量设置为给定模块名的父模块(除了它已经做过的所有其他事情)。

用例

-m标志有两个值得注意的用例:

  1. 从命令行执行可能不知道其文件名的模块。这个用例利用了 Python 解释器知道如何将模块名转换为文件名的事实。当您想从命令行运行 stdlib 模块或 3rd-party 模块时,这尤其有利。例如,很少有人知道http.server模块的文件名,但大多数人知道它的模块名,因此我们可以使用python -m http.server.

  2. 执行包含绝对或相对导入的本地包而无需安装它。这个用例在PEP 338 中有详细说明,并利用了当前工作目录被添加到sys.path而不是模块目录的事实。此用例与用于pip install -e .在开发/编辑模式下安装包非常相似。

缺点

尽管-m多年来进行了所有改进,但它仍然有一个主要缺点——它只能执行用 Python 编写的模块(即,*.py)。例如,如果-m用于执行 C 编译代码模块,则会产生以下错误,No code object available for <modulename>(更多详细信息请参见此处)。

详细比较

通过导入语句(即import <modulename>)执行模块:

  • sys.path以任何方式修改
  • __name__ 设置为绝对形式 <modulename>
  • __package__ 设置为直接父包 <modulename>
  • __init__.py 对所有包进行评估(包括它自己的包模块)
  • __main__.py评价包模块; 代码针对代码模块进行评估

通过带有文件名(即python <filename>)的命令行执行模块:

  • sys.path 修改为包含最终目录 <filename>
  • __name__ 被设定为 '__main__'
  • __package__ 被设定为 None
  • __init__.py 不对任何包进行评估(包括它自己的包模块)
  • __main__.py评估包模块;代码针对代码模块进行评估。

通过带有模块名(即python -m <modulename>)的命令行执行模块:

  • sys.path 修改为包含当前目录
  • __name__ 被设定为 '__main__'
  • __package__ 设置为直接父包 <modulename>
  • __init__.py 对所有包进行评估(包括它自己的包模块)
  • __main__.py评估包模块;代码针对代码模块进行评估

结论

-m简单来说,该标志是一种通过使用模块名而不是文件名从命令行执行 python 脚本的方法。-m然而,的真正强大之处在于它能够将import语句的强大功能(例如,支持显式相对导入和自动包__init__评估)与命令行的便利性结合起来。

  • 这是我读过的关于该主题的最全面的演示。谢谢! (4认同)
  • 我认为这句话应该修改: *“例如,如果通过`python &lt;filename&gt;`执行一个包模块,那么`&lt;filename&gt;/__init__.py`将被评估,然后是`&lt;filename&gt;/__main__.py。 `"* - 您是否尝试说&lt;dirname&gt;?如果是这种情况,则只会执行`&lt;dirname&gt;/__main__.py`。 (2认同)
  • @starriet哦,你是对的。我确实以为我已经测试过了,但是当我再次尝试时,它的表现就像你说的那样。哦,看来我最后做对了。我想我没有回去修复它。是的,我使用 &lt;filename&gt; 通常表示任何路径(包括目录)。 (2认同)

小智 11

我认为值得一提的是,执行时

python -m some_package some_arguments
Run Code Online (Sandbox Code Playgroud)

python解释器将__main__.py在包路径中查找要执行的文件。等效于:

python path_to_package/__main__.py somearguments
Run Code Online (Sandbox Code Playgroud)

它将在以下时间执行内容:

if __name__ == "__main__":
Run Code Online (Sandbox Code Playgroud)

如果文件不存在,则不能直接执行此程序包。

  • 包初始化文件怎么样?当主文件存在时,init 也会被调用吗? (2认同)

yyF*_*red 8

我只想提一个可能令人困惑的案例。

假设您用来pip3安装一个包foo,其中包含一个bar模块。所以这意味着您可以python3 -m foo.bar从任何目录执行。另一方面,你有一个像这样的目录结构:

src
|
+-- foo
    |
    +-- __init__.py
    |
    +-- bar.py
Run Code Online (Sandbox Code Playgroud)

你在src/。当您运行 时python -m foo.bar,您正在运行bar.py, 而不是已安装的模块。但是,如果您从任何其他目录调用python -m foo.bar,则您正在使用已安装的模块。

python如果您使用代替 ,这种行为肯定不会发生python -m,并且可能会让初学者感到困惑。原因是 Python 搜索模块的顺序。


小智 5

简而言之,“python -m”开关的最佳用例之一是当您想要告诉 Python 您想要运行模块而不是执行 .py 文件时。

考虑以下示例:名为“venv”的文件(不带“.py”文件扩展名)中有一个 Python 脚本。如果您发出此命令:

python venv
Run Code Online (Sandbox Code Playgroud)

然后,Python将执行当前目录中的“venv”文件。但是,如果您想使用“python venv”模块创建一个新的虚拟环境,您可以运行:

python -m venv
Run Code Online (Sandbox Code Playgroud)

在这种情况下,Python 将运行“venv”模块,而不是“venv”文件。

另一个例子,如果你想运行 Pyhton 的内置本地 http 服务器并发出命令:

python http.server
Run Code Online (Sandbox Code Playgroud)

你会得到如下错误:

python: can't open file '/home/user/http.server': [Errno 2] No such file or directory
Run Code Online (Sandbox Code Playgroud)

那是因为 Python 尝试执行一个名为“http.server”的文件,但没有找到它。因此,您想发出相同的命令,但使用“-m”开关:

python -m http.server
Run Code Online (Sandbox Code Playgroud)

这样Python就知道你想要模块'http.server'而不是文件。


归档时间:

查看次数:

67309 次

最近记录:

6 年,6 月 前