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

时间:2014-03-07 04:25:39

标签: python module package

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

使用此python代码a.py:

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

我测试了python -m a来获取

"" <-- Empty String
__main__

python a.py返回

None <-- None
__main__

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

有趣的是,对于python -m runpy a,我得到与python -m a相同的python模块编译得到a.pyc。

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

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

3 个答案:

答案 0 :(得分:98)

当您使用-m command-line flag时,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 bin/python -m foo.bar.baz 
foo.bar
__main__

因此,在使用-m开关时,Python必须真正关心包。普通脚本永远不能 一个包,因此__package__设置为None

但是使用-m在包内运行包或模块,现在至少存在包的可能性,因此{{1}变量设置为字符串值;在上面的演示中,它被设置为__package__,对于不在包内的普通模块,它被设置为空字符串。

至于foo.bar 模块; Python导入的脚本与常规模块一样运行。创建一个新的模块对象来保存存储在__main__中的全局命名空间。这是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__ 一起运行时,Python会查找该包中包含的-m模块并将其作为脚本执行。它的名称仍设置为__main__,模块对象仍存储在__main__中。

答案 1 :(得分:14)

使用-m选项执行Python代码

使用-m标志。

当你有一个脚本时,结果几乎一样,但是当你开发一个没有-m标志的包时,如果你想运行一个子包,或者没有办法让导入正常工作包中的模块作为程序的主要入口点(相信我,我已经尝试过了。)

文档

就像docs说:

  

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

  

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

所以

python -m pdb

大致相当于

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

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

说明:

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

  

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

一些python代码应该是run as a module:(我认为这个例子比命令行选项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

And from the release note highlights for Python 2.4

  

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

后续问题

  

另外,David Beazley的Python Essential Reference将其解释为“The   -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...

来自the docs

  

<强> -I

     

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

__package__做什么?

它支持明确的相对导入,但与此问题没有特别密切关系 - 请参阅此答案:What's the purpose of the "__package__" attribute in Python?

答案 2 :(得分:0)

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

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

如果用户更喜欢命令行脚本的“经典”入口点,可以在PATH中的某个地方轻松添加这些入口点,或者pip可以在安装时使用setup.py中的entry_points参数创建这些入口点。

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