【发布时间】:2011-05-01 20:53:43
【问题描述】:
__main__.py 文件的用途是什么,我应该在其中放入什么样的代码,什么时候应该有?
【问题讨论】:
-
另见文档中的main-py-in-python-packages部分。
标签: python
__main__.py 文件的用途是什么,我应该在其中放入什么样的代码,什么时候应该有?
【问题讨论】:
标签: python
通常,Python 程序通过在命令行上命名 .py 文件来运行:
$ python my_program.py
您还可以创建一个包含代码的目录或 zip 文件,并包含 __main__.py。然后你可以简单地在命令行命名目录或压缩文件,它会自动执行__main__.py:
$ python my_program_dir
$ python my_program.zip
# Or, if the program is accessible as a module
$ python -m my_program
您必须自己决定您的应用程序是否可以从这样的执行中受益。
注意__main__ 模块 通常不是来自__main__.py 文件。可以,但通常不会。当您运行像python my_program.py 这样的脚本时,该脚本将作为__main__ 模块而不是my_program 模块运行。这也发生在以 python -m my_module 或其他几种方式运行的模块上。
如果您在错误消息中看到名称 __main__,这并不一定意味着您应该查找 __main__.py 文件。
【讨论】:
python -m program_dir 和python program_dir 有点不同:后者从不在目录中运行__init__.py(如果有的话)。
python3 program_dir,它运行了__init__.py。
python3 dir 运行__main__.py 但不是__init__.py,而python3 -m dir 运行两者。
__main__.py 中有一些代码触发了__init__.py 的导入
__main__.py放在项目的根目录下,这样开发者就可以运行python .,瞧!
__main__.py 文件的用途是什么?在创建 Python 模块时,通常使模块在作为程序的入口点运行时执行某些功能(通常包含在 main 函数中)。这通常通过大多数 Python 文件底部的以下常用习语来完成:
if __name__ == '__main__':
# execute only if run as the entry point into the program
main()
您可以使用__main__.py 获得与 Python 包相同的语义,它可能具有以下结构:
.
└── demo
├── __init__.py
└── __main__.py
要查看此内容,请将以下内容粘贴到 Python 3 shell 中:
from pathlib import Path
demo = Path.cwd() / 'demo'
demo.mkdir()
(demo / '__init__.py').write_text("""
print('demo/__init__.py executed')
def main():
print('main() executed')
""")
(demo / '__main__.py').write_text("""
print('demo/__main__.py executed')
from demo import main
main()
""")
我们可以把demo当作一个包来实际导入,它执行__init__.py中的顶层代码(但不是main函数):
>>> import demo
demo/__init__.py executed
当我们使用包作为程序的入口点时,我们执行__main__.py中的代码,它首先导入__init__.py:
$ python -m demo
demo/__init__.py executed
demo/__main__.py executed
main() executed
您可以从文档中得出这一点。 documentation 说:
__main__— 顶级脚本环境
'__main__'是顶级代码执行的范围的名称。 从标准读取时,模块的__name__设置为等于'__main__'输入、脚本或来自交互式提示。一个模块可以发现它是否在主范围内运行 通过检查它自己的
__name__,它允许一个常见的习惯用法 当模块作为脚本运行时,有条件地执行模块中的代码或 使用python -m,但在导入时没有:if __name__ == '__main__': # execute only if run as a script main()对于一个包,同样的效果可以通过包含一个
__main__.py模块,其内容会在以-m运行模块时执行。
您也可以将此目录(包括__main__.py)压缩到一个文件中,然后像这样从命令行运行它 - 但请注意,压缩包不能执行子包或子模块作为入口点:
from pathlib import Path
demo = Path.cwd() / 'demo2'
demo.mkdir()
(demo / '__init__.py').write_text("""
print('demo2/__init__.py executed')
def main():
print('main() executed')
""")
(demo / '__main__.py').write_text("""
print('demo2/__main__.py executed')
from __init__ import main
main()
""")
注意细微的变化——我们从__init__ 导入main 而不是demo2——这个压缩目录不被视为一个包,而是一个脚本目录。所以它必须在没有-m标志的情况下使用。
与问题特别相关 - zipapp 导致压缩目录默认执行 __main__.py - 它在 __init__.py 之前首先执行:
$ python -m zipapp demo2 -o demo2zip
$ python demo2zip
demo2/__main__.py executed
demo2/__init__.py executed
main() executed
再次注意,这个压缩目录不是一个包——你也不能导入它。
【讨论】:
__main__.py 中使用if __name__ == '__main__' 吗?还是没有必要?
__main__.py 用于 zip 文件中的 python 程序。 __main__.py 文件将在 zip 文件运行时执行。例如,如果 zip 文件是这样的:
test.zip
__main__.py
而__main__.py的内容是
import sys
print "hello %s" % sys.argv[1]
如果我们要运行 python test.zip world,我们会得到 hello world。
所以__main__.py 文件在对 zip 文件调用 python 时运行。
【讨论】:
您在yourpackage 中创建__main__.py 以使其可执行为:
$ python -m yourpackage
【讨论】:
-m 仅在程序可作为模块访问时有效,否则您可以使用 python <yourpackage> 注意:没有 -m 选项
package?
python -m <yourproject> 不起作用,-m 是一个冗余选项,但 @987654331 @ 效果很好。
a 执行并假设脚本a/b/c/__main__.py...python -m b.c 将从目录a 执行,并且主脚本的导入将相对于a。但是python b/c 将从目录c 的导入范围执行,因此像import b.d 这样的主脚本中的任何导入都将失败。
-m 开关的情况下运行之间有一个非常重要的区别。我在下面添加了一个答案来澄清这一点,供任何想知道差异的未来读者使用。
这里的一些答案暗示给定一个包含__main__.py 文件的“包”目录(有或没有明确的__init__.py 文件),使用-m 开关或运行该目录没有区别没有。
最大的区别在于没有-m开关,“包”目录先添加到路径(即sys.path),然后文件正常运行,没有包语义。
而使用 -m 开关,尊重包语义(包括相对导入),并且包目录本身永远不会添加到系统路径。
这是一个非常重要的区别,无论是相对导入是否有效,但更重要的是规定在系统模块意外隐藏的情况下将导入什么。
例子:
考虑一个名为PkgTest 的目录,其结构如下
:~/PkgTest$ tree
.
├── pkgname
│ ├── __main__.py
│ ├── secondtest.py
│ └── testmodule.py
└── testmodule.py
__main__.py 文件的内容如下:
:~/PkgTest$ cat pkgname/__main__.py
import os
print( "Hello from pkgname.__main__.py. I am the file", os.path.abspath( __file__ ) )
print( "I am being accessed from", os.path.abspath( os.curdir ) )
from testmodule import main as firstmain; firstmain()
from .secondtest import main as secondmain; secondmain()
(其他文件定义类似,打印输出类似)。
如果您在没有-m 开关的情况下运行此程序,您将得到这样的结果。请注意,相对导入失败,但更重要的是请注意选择了错误的测试模块(即相对于工作目录):
:~/PkgTest$ python3 pkgname
Hello from pkgname.__main__.py. I am the file ~/PkgTest/pkgname/__main__.py
I am being accessed from ~/PkgTest
Hello from testmodule.py. I am the file ~/PkgTest/pkgname/testmodule.py
I am being accessed from ~/PkgTest
Traceback (most recent call last):
File "/usr/lib/python3.6/runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
File "/usr/lib/python3.6/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "pkgname/__main__.py", line 10, in <module>
from .secondtest import main as secondmain
ImportError: attempted relative import with no known parent package
而使用 -m 开关,你会得到你(希望)所期望的:
:~/PkgTest$ python3 -m pkgname
Hello from pkgname.__main__.py. I am the file ~/PkgTest/pkgname/__main__.py
I am being accessed from ~/PkgTest
Hello from testmodule.py. I am the file ~/PkgTest/testmodule.py
I am being accessed from ~/PkgTest
Hello from secondtest.py. I am the file ~/PkgTest/pkgname/secondtest.py
I am being accessed from ~/PkgTest
注意:老实说,应该避免在没有-m 的情况下运行。事实上,我会更进一步说,我会创建任何executable packages,除非通过-m 开关运行,否则它们会失败。
换句话说,我只会通过“相对导入”显式地从“包内”模块导入,假设所有其他导入都代表系统模块。如果有人尝试在没有-m 开关的情况下运行您的包,则相对导入语句将引发错误,而不是静默运行错误的模块。
【讨论】:
testmodule 来自PkgTest/pkgname 的那个,你将如何强制使用-m?
from . testmodule import main(注意点!)。或者,您可以明确使用包名称并说,例如from pkgname import testmodule。如果您想在代码中导入 both,可以使用不同的名称加载它们,例如import testmodule as testmodule1; from pkgname import testmodule as testmodule2.
如果您的脚本是目录或 ZIP 文件而不是单个 python 文件,则当“脚本”作为参数传递给 python 解释器时,__main__.py 将被执行。
【讨论】: