__main__ --- 最高层级代码环境— Python 3.10.7 說明文件

文章推薦指數: 80 %
投票人數:10人

在Python 中,特殊名称 __main__ 被用于两个重要的构造: 程序的最高层级环境的名称,可以 ... bandclass ├── __init__.py ├── __main__.py └── student.py. 瀏覽 索引 模組| 下一頁| 上一頁| Python» 3.10.7Documentation» Python標準函式庫(StandardLibrary)» Python运行时服务» __main__---最高层级代码环境 | __main__---最高层级代码环境¶ 在Python中,特殊名称__main__被用于两个重要的构造: 程序的最高层级环境的名称,可以使用__name__=='__main__'表达式来检查它;以及 Python包中的__main__.py文件。

这两种机制都有Python模块有关;用户如何与它们交互以及它们之间如何交互。

下文将进行详细说明。

如果你还不了解Python模块,请查看教程模組(Module)一节的介绍。

__name__=='__main__'¶ 当一个Python模块或包被导入时,__name__会被设为模块的名称。

通常,这将是Python文件本身的名称去掉.py后缀: >>>importconfigparser >>>configparser.__name__ 'configparser' 如果文件是包的组成部分,则__name__还将包括父包的路径: >>>fromconcurrent.futuresimportprocess >>>process.__name__ 'concurrent.futures.process' 不过,如果模块是在最高层级代码环境中执行的,则它的__name__会被设为字符串'__main__'。

什么是“最高层级代码环境”?¶ __main__是最高层级代码运行所在环境的名称。

“最高层级代码”即用户指定最先启动运行的Python模块。

它被称为“最高层级”是因为它将导入程序所需的所有其他模块。

有时“最高层级代码”也被称为应用的入口点。

最高层级代码环境可以是: 一个交互提示符的作用域: >>>__name__ '__main__' 作为文件参数传给Python解释器的Python模块: $python3helloworld.py Hello,world! 作为-m参数传给Python解释器的Python模块或包: $python3-mtarfile usage:tarfile.py[-h][-v](...) Python解释器从标准输入中读取的Python代码: $echo"importthis"|python3 TheZenofPython,byTimPeters Beautifulisbetterthanugly. Explicitisbetterthanimplicit. ... 作为-c参数传递给Python解释器的Python代码: $python3-c"importthis" TheZenofPython,byTimPeters Beautifulisbetterthanugly. Explicitisbetterthanimplicit. ... 在以上每个情形中,顶级模块的__name__被设置为'__main__'。

因此,一个模块可以通过检查自己的__name__,来发现它是否在顶层环境中运行。

这是允许在模块没有从导入语句中初始化的情况下,有条件地执行代码的一个常见的语句: if__name__=='__main__': #Executewhenthemoduleisnotinitializedfromanimportstatement. ... 也參考 关于在所有情况下__name__是如何设置的细节,请看教程部分模組(Module)。

常见用法¶ 有些模块包含了仅供脚本使用的代码,比如解析命令行参数或从标准输入获取数据。

如果这样的模块被从不同的模块中导入,例如为了单元测试,脚本代码也会无意中执行。

这就是if__name__=='__main__'代码块的用武之地。

除非模块在顶层环境中被执行,否则该块内的代码不会运行。

将尽可能少的语句放在下面的if__name___=='__main__'块中可以提高代码的清晰度和正确性。

最常见的,一个名为main的函数封装了程序的主要行为: #echo.py importshlex importsys defecho(phrase:str)->None: """Adummywrapperaroundprint.""" #fordemonstrationpurposes,youcanimaginethatthereissome #valuableandreusablelogicinsidethisfunction print(phrase) defmain()->int: """Echotheinputargumentstostandardoutput""" phrase=shlex.join(sys.argv) echo(phrase) return0 if__name__=='__main__': sys.exit(main())#nextsectionexplainstheuseofsys.exit 请注意,如果模块没有将代码封装在main函数内,而是直接放在if__name__=='__main__'块内,那么这个phrase变量对整个模块来说就是全局变量。

这很容易出错,因为模块内的其他函数可能会无意中使用全局变量而不是局部名称。

一个main函数解决了这个问题。

使用main函数有一个额外的好处,就是echo函数本身是孤立的,可以在其他地方导入。

当echo.py被导入时,echo和main函数将被定义,但它们都不会被调用,因为__name__!='__main__'。

打包考量¶ main函数经常被用来创建命令行工具,把它们指定为控制台脚本的入口点。

当这样做时,pip将函数调用插入到模板脚本中,其中main的返回值被传递到sys.exit()。

例如: sys.exit(main()) 由于main调用被包裹在sys.exit()中,期望你的函数将返回一些可被sys.exit()作为输入而接受的值;通常为一个整数或None(如果你的函数没有返回语句,则隐含返回)。

通过主动遵循这一惯例,我们的模块在直接运行时(即python3echo.py)会有相同的行为,如果我们以后把它打包成可用pip安装的包中的控制台脚本入口,它也会有相同的行为。

特别的是,要小心从你的main函数中返回字符串。

sys.exit()将把一个字符串参数解释为失败信息,所以你的程序将有一个1的退出代码,表示失败。

并且这个字符串将被写入sys.stderr。

前面的echo.py例子举例说明了使用sys.exit(main())的约定。

也參考 Python打包用户指南包含了一系列关于如何用现代工具分发和安装Python包的教程和参考资料。

Python包中的__main__.py¶ 如果你不熟悉Python包,请参阅本教程的套件(Package)一节。

最常见的是,__main__.py文件被用来为一个包提供命令行接口。

假设有下面这个虚构的包,"bandclass": bandclass ├──__init__.py ├──__main__.py └──student.py 当使用-m标志从命令行直接调用软件包本身时,将执行__main__.py。

比如说。

$python3-mbandclass 这个命令将导致__main__.py的运行。

你如何利用这一机制将取决于你所编写的软件包的性质,但在这个假设的案例中,允许教师搜索学生可能是有意义的: #bandclass/__main__.py importsys from.studentimportsearch_students student_name=sys.argv[2]iflen(sys.argv)>=2else'' print(f'Foundstudent:{search_students(student_name)}') 注意,from.studentimportsearch_students是一个相对导入的例子。

这种导入方式可以在引用一个包内的模块时使用。

更多细节,请参见教程模組(Module)中的套件內引用一节。

常见用法¶ __main__.py的内容通常不是用if__name__=='__main__'区块围起来的。

相反,这些文件保持简短,功能从其他模块执行。

那些其他模块可以很容易地进行单元测试,并且可以适当地重复使用。

如果使用,一个if__name__=='__main__'区块仍然会像预期的那样对包内的__main__.py文件起作用,因为如果导入,它的__name__属性将包括包的路径: >>>importasyncio.__main__ >>>asyncio.__main__.__name__ 'asyncio.__main__' 但这对.zip文件的根目录中的__main__.py文件不起作用。

因此,为了保持一致性,像下面提到的venv这样的最小__main__.py是首选。

也參考 参见venv以了解标准库中最小__main__.py的软件包示例。

它不包含一个if__name__=='__main__'块。

你可以用python3-mvenv[directory]调用它。

参见runpy以了解更多关于-m标志对解释器可执行包的细节。

参见zipapp了解如何运行打包成.zip文件的应用程序。

在这种情况下,Python会在归档文件的根目录下寻找一个__main__.py文件。

import__main__¶ 不管Python程序是用哪个模块启动的,在同一程序中运行的其他模块可以通过导入__main__模块来导入顶级环境的范围(namespace)。

这并不是导入一个__main__.py文件,而是导入使用特殊名称'__main__'的哪个模块。

下面是一个使用__main__命名空间的模块的例子: #namely.py import__main__ defdid_user_define_their_name(): return'my_name'indir(__main__) defprint_user_name(): ifnotdid_user_define_their_name(): raiseValueError('Definethevariable`my_name`!') if'__file__'indir(__main__): print(__main__.my_name,"foundinfile",__main__.__file__) else: print(__main__.my_name) 该模块的用法示例如下: #start.py importsys fromnamelyimportprint_user_name #my_name="Dinsdale" defmain(): try: print_user_name() exceptValueErrorasve: returnstr(ve) if__name__=="__main__": sys.exit(main()) 现在,如果我们启动我们的程序,结果会是这样的: $python3start.py Definethevariable`my_name`! 该程序的退出代码为1,表明有错误。

取消对my_name="Dinsdale"这一行的注释,就可以修复程序,现在它的退出状态代码为0,表示成功。

$python3start.py Dinsdalefoundinfile/path/to/start.py 请注意,导入__main__不会导致无意中运行旨在用于脚本的顶层代码的问题,这些代码被放在模块start的if__name__=="__main__"块中。

为什么这样做? Python解释器启动时会在sys.modules中插入一个空的__main__模块,并通过运行顶层代码来填充它。

在我们的例子中这就是start模块,它逐行运行并导入namely。

反过来,namely会导入__main__(这其实是start)。

这就是一个导入循环!幸运的是,由于部分填充的__main__模块存在于sys.modules中,Python会将其传递给namely。

请参阅导入系统参考资料中有关__main__的特别考量来了解其中的详情。

PythonREPL是另一个"顶层环境"的例子,所以在REPL中定义的任何东西都成为__main__范围的一部分: >>>importnamely >>>namely.did_user_define_their_name() False >>>namely.print_user_name() Traceback(mostrecentcalllast): ... ValueError:Definethevariable`my_name`! >>>my_name='Jabberwocky' >>>namely.did_user_define_their_name() True >>>namely.print_user_name() Jabberwocky 注意,在这种情况下,__main__范围不包含__file__属性,因为它是交互式的。

__main__范围用于pdb和rlcompleter的实现。

目录 __main__---最高层级代码环境 __name__=='__main__' 什么是“最高层级代码环境”? 常见用法 打包考量 Python包中的__main__.py 常见用法 import__main__ 上個主題 builtins---內建物件 下個主題 warnings——警告信息的控制 此頁面 回報錯誤 顯示原始碼 瀏覽 索引 模組| 下一頁| 上一頁| Python» 3.10.7Documentation» Python標準函式庫(StandardLibrary)» Python运行时服务» __main__---最高层级代码环境 |



請為這篇文章評分?