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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22241420/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 00:33:55  来源:igfitidea点击:

Execution of Python code with -m option or not

pythonmodulepackage

提问by prosseek

The python interpreter has -mmoduleoption that "Runs library module moduleas a script".

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

With this python code a.py:

使用这个python代码a.py:

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

I tested python -m ato get

我测试python -m a得到

"" <-- Empty String
__main__

whereas python a.pyreturns

python a.py返回

None <-- None
__main__

To me, those two invocation seems to be the same except __package__ is not None when invoked with -m option.

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

Interestingly, with python -m runpy a, I get the same as python -m awith python module compiled to get a.pyc.

有趣的是,使用python -m runpy a,我得到的结果与python -m a编译得到 a.pyc 的 python 模块相同。

What's the (practical) difference between these invocations? Any pros and cons between them?

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

Also, David Beazley's Python Essential Reference explains it as "The -m option runs a library module as a script which executes inside the __main__ module prior to the execution of the main script". What does it mean?

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

采纳答案by Martijn Pieters

When you use the -mcommand-line flag, Python will import a module or packagefor you, then run it as a script. When you don't use the -mflag, the file you named is run as just a script.

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

The distinction is important when you try to run a package. There is a big difference between:

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

python foo/bar/baz.py

and

python -m foo.bar.baz

as in the latter case, foo.baris imported and relative imports will work correctly with foo.baras the starting point.

与后一种情况一样,foo.bar导入和相对导入将foo.bar作为起点正常工作。

Demo:

演示:

$ 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__

As a result, Python has to actually care about packages when using the -mswitch. A normal script can never bea package, so __package__is set to None.

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

But run a package or module insidea package with -mand now there is at least the possibilityof a package, so the __package__variable is set to a string value; in the above demonstration it is set to foo.bar, for plain modules not inside a package, it is set to an empty string.

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

As for the __main__module; Python imports scripts being run as it would a regular module. A new module object is created to hold the global namespace, stored in sys.modules['__main__']. This is what the __name__variable refers to, it is a key in that structure.

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

For packages, you can create a __main__.pymodule and have that run when running python -m package_name; in fact that's the only way you canrun a package as a script:

对于包,您可以创建一个__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__

So, when naming a package for running with -m, Python looks for a __main__module contained in that package and executes that as a script. It's name is then still set to __main__, and the module object is still stored in sys.modules['__main__'].

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

回答by Aaron Hall

Execution of Python code with -m option or not

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

Use the -mflag.

使用-m旗帜。

The results are pretty much the same when you have a script, but when you develop a package, without the -mflag, there's no way to get the imports to work correctly if you want to run a subpackage or module in the package as the main entry point to your program (and believe me, I've tried.)

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

The docs

文档

Like the docs on the -m flagsay:

就像-m 标志上文档说:

Search sys.path for the named module and execute its contents as the __main__module.

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

and

As with the -c option, the current directory will be added to the start of sys.path.

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

so

所以

python -m pdb

is roughly equivalent to

大致相当于

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

(assuming you don't have a package or script in your current directory called pdb.py)

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

Explanation:

解释:

Behavior is made "deliberately similar to" scripts.

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

Many standard library modules contain code that is invoked on their execution as a script. An example is the timeit module:

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

Some python code is intended to be run as a module:(I think this example is better than the commandline option doc example)

一些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

And from the release note highlights for Python 2.4:

并从 Python 2.4 的发行说明重点

The -m command line option - python -m modulename will find a module in the standard library, and invoke it. For example, python -m pdbis equivalent to python /usr/lib/python2.4/pdb.py

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

Follow-up Question

后续问题

Also, David Beazley's Python Essential Reference explains it as "The -m option runs a library module as a script which executes inside the __main__module prior to the execution of the main script".

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

It means any module you can lookup with an import statement can be run as the entry point of the program - if it has a code block, usually near the end, with if __name__ == '__main__':.

这意味着您可以使用 import 语句查找的任何模块都可以作为程序的入口点运行 - 如果它有一个代码块,通常在末尾,带有if __name__ == '__main__':.

-mwithout adding the current directory to the path:

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

A comment here elsewhere says:

其他地方的评论说:

That the -m option also adds the current directory to sys.path, is obviously a security issue (see: preload attack). This behavior is similar to library search order in Windows (before it had been hardened recently). It's a pity that Python does not follow the trend and does not offer a simple way to disable adding . to sys.path

-m 选项还将当前目录添加到 sys.path,这显然是一个安全问题(请参阅:预加载攻击)。此行为类似于 Windows 中的库搜索顺序(在最近强化之前)。遗憾的是,Python 没有跟上潮流,也没有提供一种简单的方法来禁用添加 . 到 sys.path

Well, this demonstrates the possible issue - (in windows remove the quotes):

好吧,这说明了可能的问题 - (在 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)]

Use the -Iflag to lock this down for production environments (new in version 3.4):

使用该-I标志来锁定生产环境(3.4 版中的新功能):

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

from the docs:

文档

-I

Run Python in isolated mode. This also implies -E and -s. In isolated mode sys.path contains neither the script's directory nor the user's site-packages directory. All PYTHON* environment variables are ignored, too. Further restrictions may be imposed to prevent the user from injecting malicious code.

-I

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

What does __package__do?

有什么作用__package__

It enables explicit relative imports, not particularly germane to this question, though - see this answer here: What's the purpose of the "__package__" attribute in Python?

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

回答by ddbug

The main reason to run a module (or package) as a script with -m is to simplify deployment, especially on Windows. You can install scripts in the same place in the Python library where modules normally go - instead of polluting PATH or global executable directories such as ~/.local (the per-user scripts directory is ridiculously hard to find in Windows).

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

Then you just type -m and Python finds the script automagically. For example, python -m pipwill find the correct pip for the same instance of Python interpreter which executes it. Without -m, if user has several Python versions installed, which one would be the "global" pip?

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

If user prefers "classic" entry points for command-line scripts, these can be easily added as small scripts somewhere in PATH, or pip can create these at install time with entry_points parameter in setup.py.

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

So just check for __name__ == '__main__'and ignore other non-reliable implementation details.

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