https://docs.python.org/3/library/__main__.html
Python中__main__用于标记顶层代码环境,有两种使用方式:
if __name__ == '__main__'__main__.py
顶层代码环境
顶层代码环境是一个名为__main__的模块,xxx.py文件被import后会成为module,module的__name__属性通常为.py的全限定名称xxx,有时会直接运行xxx.py文件,此时__name__会被置为__main__,表示本次运行该模块作为顶层代码环境。
# demo.py,python demo.py
import configparser
from concurrent.futures import process
print(configparser.__name__) # configparser
print(process.__name__) # concurrent.futures.process
print(__name__) # __main__ 通常应为demo
五种形成顶层代码环境的情形
交互式解释器
使用
python命令进入Python解释器,当前所处即为顶层代码环境__main__模块>>> __name__ '__main__'将
.py文件作为pyhton命令参数,即直接执行Python文件# demo.py,python demo.py print(__name__) # __main__ 通常应为demo将模块或包作为
python命令的-m参数,即以模块方式执行Python文件# demo.py,python -m demo print(__name__) # __main__ 通常应为demopython执行从标准输入来的程序$ echo "import this" | python # The Zen of Python, by Tim Peters # # Beautiful is better than ugly. #Explicit is better than implicit. ...python执行-c参数指定的命令$ python -c "print(1, 2, 3, __name__)" # 1 2 3 __main__
使用示例1
在一些情况下,一个.py文件即被其他文件导入又充当可执行脚本,由于将.py文件导入作为模块时会执行整个.py文件,而实际上部分代码应当只在作为脚本执行时才执行,此时通常使用if __name__ == '__main__'来判断是否是顶层代码环境,特别注意,if __name__ == '__main__'其下代码属于全局作用域,即使不执行其中定义的全局变量会影响其余代码,通常使用main函数(名称任意,习惯上使用main)作为入口函数以避免该影响。
错误示例
# echo.py
import shlex
import sys
def echo(phrase: str) -> None:
"""
通常是核心业务代码
"""
print(phrase)
phrase = shlex.join(sys.argv)
echo(phrase) # 'E:\python\pyBaseCode\echo.py'
错误示例
# echo.py
import shlex
import sys
def echo(phrase: str) -> None:
"""
通常是核心业务代码
"""
print(phrase)
if __name__ == '__main__':
phrase = shlex.join(sys.argv)
echo(phrase) # 'E:\python\pyBaseCode\echo.py'
正确示例
# echo.py
import shlex
import sys
def echo(phrase: str) -> None:
"""
通常是核心业务代码
"""
print(phrase)
def main() -> int:
phrase = shlex.join(sys.argv)
echo(phrase)
return 0
if __name__ == '__main__':
sys.exit(main())
# 'E:\python\pyBaseCode\echo.py'
__main__.py
__main__.py文件放在包下,以Python标准库venv为例:
venv
├── scripts
├── __init__.py
└── __main__.py
当使用python -m形式执行模块时会执行__main__.py
# 创建虚拟环境
python -m venv c:\path\to\myenv
__mian__.py文件通常保持代码简洁不包含过多逻辑,一般仅提供入口函数,如venv/__main__.py简短且不使用if__name__=='__main__',asyncio/__main__.py较venv/__main__.py复杂,其使用if __name__ == '__main__'但整体上也仅提供入口函数。
venv/__main__.py
https://github.com/python/cpython/blob/3.12/Lib/venv/__main__.py
import sys
from . import main
rc = 1
try:
main()
rc = 0
except Exception as e:
print('Error: %s' % e, file=sys.stderr)
sys.exit(rc)
在__main__.py中使用``依然生效,直接导入为其全限定名称
import asyncio.__main__
print(asyncio.__main__.__name__) # 'asyncio.__main__'
import __main__
Python程序中顶层代码环境被具象为__main__模块,一次程序运行中有且只有一个__main__模块,其他模块可以通过import __main__将顶层代码环境导入。__main__模块机制:
# namely.py
import __main__
print(f"is 'my_name' in dir(__main__)? {'my_name' in dir(__main__)}")
def print_user_name():
if not did_user_define_their_name():
raise ValueError('Define the variable `my_name`!')
if '__file__' in dir(__main__):
print(__main__.my_name, "found in file", __main__.__file__)
else:
print(__main__.my_name)
# special.py
import __main__
print("in special")
# start.py
import sys
from namely import print_user_name
my_name = "yuzao"
def main():
try:
print_user_name()
except ValueError as ve:
return str(ve)
if __name__ == "__main__":
sys.exit(main())
import special
# python start.py执行结果如下:
# is 'my_name' in dir(__main__)? False
# yuzao found in file E:\python\pyBaseCode\start.py
Python在解释器启动时在
sys.modules中初始化一个空白__main__模块作为顶层代码环境,然后会逐行执行初始代码(见五种形成顶层代码环境的情形)来动态填充__main__模块以
python start.py为例,会以start.py中代码为基础动态填充__main__模块根据
start.py代码逐行执行首先
import sys然后
from namely import print_user_name,此时会导入namely模块导入
namely模块时首先会导入__main__,由于最开始__main__以空白状态初始化完成所以此时不会造成循环导入,同理,尽管start.py中有if __name__ == "__main__",但__main__初始化时是空白的且此时是第二次导入,完全与if __name__ == "__main__"无关由于此时
__main__模块中并未执行到my_name = "yuzao",所以print(f"is 'my_name' in dir(__main__)? {'my_name' in dir(__main__)}")的结果自然为False随后定义
print_user_name函数完成namely的初始化和导入__main__中执行my_name = "yuzao"和定义函数main接下来执行
if __name__ == "__main__"部分,此时必然为顶层代码环境执行
main()从而调用print_user_name,显然先前my_name = "yuzao"以执行此时有输出结果yuzao found in file E:\python\pyBaseCode\start.py最后
sys.exit退出解释器以完成本次程序执行代码最后一行
import special并不会执行,先前并未导入special模块,首次导入会执行specia;.py,结果中并无in special输出,若将sys.exit去掉,仅保留main()调用,则会打印该信息is 'my_name' in dir(__main__)? False yuzao found in file E:\python\pyBaseCode\start.py in special以上,交互解释器中同理
>>> import sys >>> sys.modules {'sys': <module 'sys' (built-in)>, 'builtins': <module 'builtins' (built-in)>, '_frozen_importlib': <module '_frozen_importlib' (frozen)>, ..., 'encodings.aliases': <module 'encodings.aliases' from 'D:\\py3.11\\Lib\\encodings\\aliases.py'>, ..., '_abc': <module '_abc' (built-in)>, 'abc': <module 'abc' (frozen)>, 'io': <module 'io' (frozen)>, '__main__': <module '__main__' from '<input>'>, ...} >>> '__main__' in sys.modules True >>> import namely is 'my_name' in dir(__main__)? False >>> namely.print_user_name() Traceback (most recent call last): File "D:\PyCharm\PyCharm 2021.3.2\plugins\python\helpers\pydev\pydevconsole.py", line 364, in runcode coro = func() ^^^^^^ File "<input>", line 1, in <module> File "E:\python\pyBaseCode\namely.py", line 14, in print_user_name raise ValueError('Define the variable `my_name`!') ValueError: Define the variable `my_name`! >>> my_name = "yuzao" >>> namely.print_user_name() yuzao found in file <input>