__main__.py 是什么?
- 2025-01-08 08:50:00
- admin 原创
- 133
问题描述:
该__main__.py
文件用于什么用途,我应该在其中放入什么样的代码,以及什么时候应该有一个?
解决方案 1:
通常,Python 程序通过在命令行上命名 .py 文件来运行:
$ python my_program.py
您还可以创建一个包含代码的目录或 zip 文件,并包含__main__.py
。然后您只需在命令行上命名目录或 zip 文件,它就会__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
文件。
解决方案 2:
该文件有何__main__.py
用途?
创建 Python 模块时,通常会让模块main
在作为程序入口点运行时执行某些功能(通常包含在函数中)。这通常通过放置在大多数 Python 文件底部的以下常见习惯用法来实现:
if __name__ == '__main__':
# execute only if run as the entry point into the program
main()
对于具有的 Python 包,您可以获得相同的语义__main__.py
,它可能具有以下结构:
.
└── 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
您可以从文档中得出这一点。文档说:
__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()
""")
请注意细微的变化 - 我们从中导入main
而__init__
不是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
再次注意,这个压缩目录不是一个包 - 您也不能导入它。
解决方案 3:
这里的一些答案暗示,给定一个包含文件的“包”目录(带有或不带有明确的__init__.py
文件),__main__.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
,相对导入语句将抛出错误,而不是默默地运行错误的模块。
解决方案 4:
您创建__main__.py
它yourpackage
以使其可执行为:
$ python -m yourpackage
解决方案 5:
__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 时,该文件就会运行。
解决方案 6:
如果您的脚本是一个目录或 ZIP 文件而不是单个 python 文件,__main__.py
则当“脚本”作为参数传递给 python 解释器时将会执行。
扫码咨询,免费领取项目管理大礼包!