原文链接:https://xiets.blog.csdn.net/article/details/131407638
版权声明:原创文章禁止转载
1. 主函数 main()
许多编译型编程语言,都有一个 main()
函数(主函数)作为程序执行的入口。许多语言的 main() 函数的形参用于接收运行程序时传入的参数,并且返回一个 int 值,作为程序进程的返回状态码(一般返回0表示成功/正常退出,返回非0表示出错退出)。
下面是 C 语言的标准 main() 函数形式:
int main(int argc, char* argv[]) {
// argc 表示参数的个数, argv 表示所有参数组成的字符串数组
return 0;
}
像类似 Python 这样的解释型编程语言,程序代码是逐行解释运行的,不需要 main() 函数,也不强制使用函数来组织程序。当使用 Python 写一个项目时,为了规范化程序结构,往往也会在作为程序入口的主脚本文件中自定义一个 main() 函数作为主函数,然后再在脚本文件末尾调用该函数。
2. __name__ 属性
每一个 Python 模块中,都有一个隐含属性 __name__
,表示当前模块的名称。通常模块名称为脚本文件的文件名(不包括.py
)。
对于作为被 python 解释器直接运行的脚步文件,__name__
模块名称不是文件名,而是固定的 "__main__"
。当通过交互式模式运行 python 解释器时,__name__
的值也是 "__main__"
。
3. __main__ — 最高层级代码环境
Python 的最高层级代码环境(直接被解释器运行的脚步文件)的模块名称固定是"__main__"
,可以使用 if __name__ == "__main__"
来检查它。
“最高层级代码”即用户指定最先启动运行的 Python 模块。 它被称为“最高层级”是因为它将导入程序所需的所有其他模块。 有时“最高层级代码”也被称为应用程序的入口点。具体可参考 Python 官方文档:__main__ — 最高层级代码环境。
4. Python 中的 main() 函数
当使用 Python 写一个项目时,通常有一个脚本文件作为程序的启动脚本。启动脚本中定义一个 main() 函数,然后再在脚本文件末尾用 if __name__ == "__main__"
判断去调用它。
把下面代码保存到 main.py
文件:
import sys
def main():
print(sys.argv)
return 0
if __name__ == "__main__":
sys.exit(main())
运行脚本:
$ python3 main.py
['main.py']
为什么要使用 main()
函数,以及为什么要在 if __name__ == "__main__"
中调用 main()
函数?主要有以下几点原因:
- 规范化程序结构,参考其他编译型编程语言明确主函数入口;
- 可以把相关只作用于局部的变量定义在函数内成为局部变量,避免无差别导出为模块属性(全局变量);
- 使用
if __name__ == "__main__"
检查,可以避免主脚本文件被其他模块导入时(例如写单元测试时需要导入主模块)执行到main()
函数或相关代码,也避免因多进程编程导致重复加载模块并多次执行到main()
函数或相关代码。
下面是一段使用了多进程的代码,保存为文件 mp_main.py
:
import multiprocessing
def main():
print_text("Hello")
multiprocessing.Process(target=print_text, args=("World",)).start()
def print_text(text):
print(f"print_text: {__name__} --- {text}")
if __name__ == "__main__":
main()
运行脚本文件,可以按预期正常输出:
$ python3 mp_main.py
print_text: __main__ --- Hello
print_text: __mp_main__ --- World
从输出可以看出,多进程重新加载主模块时,模块名称不是
"__main__"
,而是"__mp_main__"
,因此重新加载主模块时,上面代码并不会重复调用main()
函数。
如果不使用 if __name__ == "__main__"
检查,脚本底部直接调用 main()
函数,运行上面代码将出错。出错的原因是因为运行脚本时启动了子进程,子进程会重新加载主模块,主模块中又调用了 main()
函数,函数中又启动了新的子进程,导致无限递归,最终出错。