2023-09-05 08:00:00

什么是__main__.py?

__main__.py文件是干什么用的,我应该把什么样的代码放进去,什么时候我应该有一个?


当前回答

通常,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文件。

其他回答

__main__.py用于zip文件中的python程序。当zip文件运行时,__main__.py文件将被执行。例如,如果zip文件是这样的:

test.zip
     __main__.py

而__main__.py的内容为

import sys
print "hello %s" % sys.argv[1]

然后如果我们要运行python test.zip world,我们会得到hello world。

因此,在zip文件上调用python时,__main__.py文件将运行。

通常,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文件。

这里的一些答案暗示,给定一个包含__main__.py文件的“包”目录(有或没有显式的__init__.py文件),在运行该目录时使用-m开关或不使用-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开关运行该程序,将得到这样的结果。请注意,相对导入失败,但更重要的是,请注意选择了错误的testmodule(即相对于工作目录):

:~/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的情况下运行。事实上,我想进一步说,我将以这种方式创建任何可执行包,除非通过-m开关运行,否则它们将失败。

换句话说,我只会通过“相对导入”显式地从“包内”模块导入,假设所有其他导入都代表系统模块。如果有人试图在没有-m开关的情况下运行您的包,相对import语句将抛出一个错误,而不是无声地运行错误的模块。

如果你的脚本是一个目录或ZIP文件,而不是一个单一的python文件,当“script”作为参数传递给python解释器时,__main__.py将被执行。

你在你的包中创建__main__.py,使其可执行:

$ python -m yourpackage