-m开关的目的是什么?

时间:2011-09-30 11:58:29

标签: python

你能解释一下调用

之间的区别吗?
python -m mymod1 mymod2.py args

python mymod1.py mymod2.py args

在这两种情况下似乎都会调用mymod1.py并且sys.argv

['mymod1.py', 'mymod2.py', 'args']

那么-m开关是什么?

4 个答案:

答案 0 :(得分:106)

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的其余部分。阅读它以获取更多信息。

答案 1 :(得分:28)

尽管我认为这个问题已经问过几次(例如herehereherehere),但没有一个完整或简洁的答案捕获-m标志的所有含义。因此,以下内容将尝试改进以前的功能。

简介(TLDR)

-m标志可以做很多事情,并非总是需要所有这些。简而言之,它可以用于:(1)通过命令行通过模块名而不是文件名执行python代码(2)将目录添加到sys.path以用于import分辨率,以及(3)执行python包含从命令行导入的相对代码。

初步

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

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

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

所有模块(代码和程序包)都可以执行(即,与模块关联的代码将由Python解释器评估)。根据执行方法(和模块类型),对哪些代码进行评估以及何时进行更改可能会有所不同。例如,如果一个人通过python <filename>执行一个软件包模块,那么将评估<filename>/__init__.py,然后是<filename>/__main__.py。另一方面,如果一个人通过import <modulename>执行相同的程序包模块,那么将仅执行程序包的__init__.py

-m

的历史发展

-m标志最初是在Python 2.4.1中引入的。最初,它的唯一目的是提供另一种方法来标识要从命令行执行的python模块。也就是说,如果我们同时知道模块的<filename><modulename>,则以下两个命令是等效的:python <filename> <args>python -m <modulename> <args>。根据{{​​3}},此迭代的一个约束是-m仅适用于顶级模块名称(即,可以直接在sys.path上找到的模块,而无需任何中间包模块)。 / p>

随着PEP 338的完成,-m功能被扩展以支持顶级之上的<modulename>表示形式。这意味着http.server之类的名称现已得到完全支持。此扩展还意味着,除了由模块名本身引用的模块之外,现在还对模块名中的每个父包进行了评估(即,对所有父包__init__.py文件进行了评估)。

PEP 338-m的最后一项主要功能增强。通过此升级,-m获得了在执行模块时不仅支持绝对导入而且还支持显式相对导入的功能。这是通过更改-m来实现的,以便将__package__变量设置为给定模块名的父模块(除了已经执行的所有其他操作)。

用例

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

  1. 从命令行执行可能不知道其文件名的模块。该用例利用了Python解释器知道如何将模块名转换为文件名的事实。当要从命令行运行stdlib模块或第三方模块时,这特别有利。例如,很少有人知道http.server模块的文件名,但是大多数人确实知道它的模块名,因此我们可以使用python -m http.server从命令行执行它。

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

注意事项

多年来对-m进行的所有增强功能仍然存在一个主要缺点-它只能执行以Python编写的模块(即*.py)。例如,如果使用-m执行C编译代码模块,则会产生以下错误No code object available for <modulename>(有关更多详细信息,请参见PEP 338)。

详细比较

通过import语句(即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进行了软件包模块评估;该代码将针对代码模块进行评估。

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

  • sys.path被修改为包括当前目录
  • __name__设置为'__main__'
  • __package__设置为<modulename>中的直接父包
  • __init__.py对所有程序包均进行评估(包括其自身的程序包模块)
  • __main__.py进行了软件包模块评估;对代码进行代码模块评估

结论

最简单的-m标志是一种使用模块名而不是文件名从命令行执行python脚本的方法。然而,-m的真正功能在于将import语句的功能(例如,支持显式相对导入和自动打包__init__评估)与以下功能的便利相结合:命令行。

答案 2 :(得分:2)

执行时,我想再提一件事

python -m some_package some_arguments

python解释器将在程序包路径中寻找一个__main__.py文件来执行。等效于:

python path_to_package/__main__.py somearguments

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

if __name__ == "__main__":

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

答案 3 :(得分:2)

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

假设您使用pip3安装了包含foo模块的软件包bar。因此,这意味着您可以从任何目录执行python3 -m foo.bar。另一方面,您具有这样的目录结构:

src
|
+-- foo
    |
    +-- __init__.py
    |
    +-- bar.py

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

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