是否使用-m选项执行Python代码


111

python解释器的-m 模块选项为“将库模块模块作为脚本运行”。

使用此python代码a.py:

if __name__ == "__main__":
    print __package__
    print __name__

我测试python -m a

"" <-- Empty String
__main__

python a.py回报

None <-- None
__main__

对我来说,这两个调用似乎是相同的,只是当使用-m选项调用__package__时不为None。

有趣的是,有了python -m runpy a,我得到了与python -m a编译成a.pyc的python模块相同的东西。

这些调用之间的(实际)区别是什么?他们之间有什么利弊吗?

同样,David Beazley的Python Essential Reference将其解释为“ -m选项将库模块作为脚本运行,该脚本在执行主脚本之前在__main__模块内部执行 ”。这是什么意思?

Answers:


169

当您使用-m命令行标志时,Python将为您导入模块或包,然后将其作为脚本运行。当您不使用该-m标志时,您命名的文件仅作为脚本运行。

当您尝试运行软件包时,区别很重要。之间有很大的区别:

python foo/bar/baz.py

python -m foo.bar.baz

与后一种情况一样,foo.bar将导入,并且相对导入将foo.bar作为起点正确运行。

演示:

$ mkdir -p test/foo/bar
$ touch test/foo/__init__.py
$ touch test/foo/bar/__init__.py
$ cat << EOF > test/foo/bar/baz.py 
> if __name__ == "__main__":
>     print __package__
>     print __name__
> 
> EOF
$ PYTHONPATH=test python test/foo/bar/baz.py 
None
__main__
$ PYTHONPATH=test python -m foo.bar.baz 
foo.bar
__main__

结果,在使用-m开关时,Python实际上必须关心软件包。普通脚本永远不能是软件包,因此__package__将其设置为None

但运行一个封装或模块与包裹-m和现在至少存在可能性的封装的,所以__package__变量设置为一个字符串值; 在上面的演示中,将其设置为foo.bar,对于不在包内的普通模块,将其设置为空字符串。

至于__main__ 模块 ; Python会像常规模块一样导入正在运行的脚本。创建一个新的模块对象来保存存储在中的全局名称空间sys.modules['__main__']。这就是__name__变量所指的,它是该结构中的关键。

对于包,您可以创建一个__main__.py模块并在运行时让其运行python -m package_name;其实这是你的唯一途径可以运行包的脚本:

$ PYTHONPATH=test python -m foo.bar
python: No module named foo.bar.__main__; 'foo.bar' is a package and cannot be directly executed
$ cp test/foo/bar/baz.py test/foo/bar/__main__.py
$ PYTHONPATH=test python -m foo.bar
foo.bar
__main__

因此,在命名要与一起运行的包时-m,Python会查找__main__该包中包含的模块并将其作为脚本执行。然后,其名称仍设置为__main__,并且模块对象仍存储在中sys.modules['__main__']


1
命令实际上是什么PYTHONPATH=test python -m foo.bar意思?您能详细解释一下吗?
安德里2008年

3
@Andriy:PYTHONPATH设置环境变量;它扩展了一系列目录,在导入时Python将在其中查找模块;在这里它将test目录添加到该系列。通过将其同一个命令行上,它适用于单一python命令。-m告诉Python导入特定模块,就像您运行一样import foo.bar。但是,__main__当您使用该开关时,Python会自动将包内的模块作为脚本运行。
马丁·皮特斯

1
having to use -m always is not that user-.friendly.我认为混合使用和不使用-m是不太用户友好的。
Simin Jie

1
@SiminJie:可以在任意路径中打开脚本,然后将其父目录添加到模块搜索路径中。-m仅适用于当前目录或已在搜索路径中注册的目录。那是我的意思。-m对于这个非常易用的问题,您没有将其提供给最终用户。
马丁·彼得斯

1
@ flow2k:我是说from Photos import ...会抱怨。也会import Photos.<something>import Photos仅适用于Python,因为它支持命名空间包(其中两个单独的发行版分别提供Photos.fooPhotos.bar分别提供,并且可以独立地对其进行管理)。
马丁·彼得斯

25

是否使用-m选项执行Python代码

使用-m标志。

当您拥有脚本时,结果几乎是相同的,但是当您开发一个没有-m标志的软件包时,如果您想运行软件包中的子软件包或模块作为主条目,则无法使导入正常工作指向您的程序(相信我,我已经尝试过了。)

该文档

就像-m标志上文档说的那样:

在sys.path中搜索指定的模块,并作为__main__模块执行其内容。

与-c选项一样,当前目录将添加到sys.path的开头。

所以

python -m pdb

大致相当于

python /usr/lib/python3.5/pdb.py

(假设您在当前目录中没有名为pdb.py的软件包或脚本)

说明:

使行为“故意类似于”脚本。

许多标准库模块包含在执行时作为脚本调用的代码。一个例子是timeit模块:

某些python代码旨在作为模块运行:(我认为此示例比命令行选项doc示例更好)

$ python -m timeit '"-".join(str(n) for n in range(100))'
10000 loops, best of 3: 40.3 usec per loop
$ python -m timeit '"-".join([str(n) for n in range(100)])'
10000 loops, best of 3: 33.4 usec per loop
$ python -m timeit '"-".join(map(str, range(100)))'
10000 loops, best of 3: 25.2 usec per loop

并且从发行说明中突出显示了Python 2.4

-m命令行选项-python -m modulename将在标准库中找到一个模块,然后调用它。例如,python -m pdb 相当于python /usr/lib/python2.4/pdb.py

后续问题

同样,David Beazley的Python Essential Reference将其解释为“ -m选项将库模块作为脚本运行,该脚本__main__在执行主脚本之前在模块内部执行”。

这意味着您可以使用import语句查找的任何模块都可以作为程序的入口点运行-如果该模块具有代码块(通常在结尾处使用)if __name__ == '__main__':

-m 而不将当前目录添加到路径:

其他地方的评论说:

-m选项还将当前目录添加到sys.path中,显然是一个安全问题(请参阅:预加载攻击)。此行为类似于Windows中的库搜索顺序(之前已对其进行了强化)。很遗憾,Python没有遵循这种趋势,并且没有提供禁用添加的简单方法。到sys.path

好吧,这演示了可能的问题-(在Windows中删除引号):

echo "import sys; print(sys.version)" > pdb.py

python -m pdb
3.5.2 |Anaconda 4.1.1 (64-bit)| (default, Jul  5 2016, 11:41:13) [MSC v.1900 64 bit (AMD64)]

使用该-I标志可将其锁定在生产环境中(版本3.4中的新增功能):

python -Im pdb
usage: pdb.py [-c command] ... pyfile [arg] ...
etc...

文档

-I

在隔离模式下运行Python。这也意味着-E和-s。在隔离模式下,sys.path既不包含脚本的目录也不包含用户的site-packages目录。所有PYTHON *环境变量也将被忽略。可能会施加进一步的限制,以防止用户注入恶意代码。

怎么__package__办?

它启用了显式相对导入,但与该问题并不特别相关-请在此处查看此答案:Python中“ __package__”属性的目的是什么?


使用-m开关时,哪个路径会添加到sys.path中?
可变

我已经引用了它:“与-c选项一样,当前目录将被添加到sys.path的开头。” 但是我已经澄清了引述的含义。
亚伦·霍尔

我的意思是-假设在D:\ test目录中,我运行命令-python -m foo.bar.boo,然后这会将python安装文件夹或D:\ test目录添加到sys.path吗?我的理解是,它将d:\ test添加到sys.path中,导入foo.bar并运行boo脚本
变量

@variable-是的,尝试一下。
亚伦·霍尔

1

使用-m将模块(或程序包)作为脚本运行的主要原因是简化部署,尤其是在Windows上。您可以将脚本安装在模块通常可以使用的Python库中的同一位置-而不污染PATH或〜/ .local等全局可执行目录(在Windows中很难找到每个用户的脚本目录)。

然后,您只需键入-m,Python就会自动找到该脚本。例如,python -m pip将为执行它的同一Python解释器实例找到正确的点。如果没有-m,那么如果用户安装了多个Python版本,哪个是“全局” pip?

如果用户更喜欢命令行脚本的“经典”入口点,则可以轻松地将它们作为小脚本添加到PATH中的某个位置,或者pip可以在安装时使用setup.py中的entry_points参数创建它们。

因此,只需检查__name__ == '__main__'并忽略其他不可靠的实现细节。


-m选项还将当前目录添加到sys.path中,显然是一个安全问题(请参阅:preload Attack)。此行为类似于Windows中的库搜索顺序(之前已对其进行了强化)。很遗憾,Python没有遵循这种趋势,并且没有提供禁用添加的简单方法。到sys.path。
ddbug
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.