你可能在一些 Python 程序中看到一种奇怪的语法,其中包括一个字符串,如 if __name__ == "__main__": main()。有些人可能会把它称为 def main if name main 的习惯用语。if __name__ == "__main__"有一些你可能想考虑的好处,特别是如果你是一个单文件 Python 脚本的爱好者。在这个关于 if __name__ == "__main__" 的教程中,我们将看看这个模式提供了什么,以及为什么使用它是明智的。
不是每个 Python 文件都是一个脚本
单独的 Python 文件可以作为库或模块的一部分,为大程序的其他部分提供服务。这些类型的文件不是为了单独或孤立地运行。例如,这段代码定义了一个函数,你可以把它导入另一个文件并在那里运行。
def library_function():
# do cool stuff
return 'cool stuff'
这意味着这段代码不会受益于 if __name__ == "__main__": main() 模式。
编程语言的模板
大多数语言都有一些模板代码,在你能写出任何有用的代码来实现某件事情之前就已经设置好了。Java程序员对这种需要的模板代码再熟悉不过了。
class HelloWorld {
public static void main(String args[]) {
System.out.println("Hello World");
}
}
在C++中,你经常看到这种模板代码来定义主函数。
#include <iostream>
int main() {
std::cout << "Hello, C++!";
return 0;
}
如果你选择的话,Python足够灵活,可以让你省略任何模板。当你简单地运行这个文件时,在一个单独的 Python 文件中的这些代码运行得很好。这就是所谓的脚本。
print('It works!')
这使得Python对初学者来说非常容易理解,因为它是如此灵活。你可以在短短的几秒钟内得到一些工作。
Python __name__ 变量
要理解 if __name__ == "__main__": main() 是如何工作的,我们首先需要理解**__name__**变量在 Python 中是什么。如果我们在一个Python文件中把它打印出来,会显示如下。
other_lib.py
print(f'__name__ = {__name__}')
__name__ = __main__
当Python运行一个文件时,在它甚至运行任何代码之前,它会设置一些特殊的变量。__name__ 变量就是这些特殊变量中的一个。当 Python 直接运行一个 Python 文件时,作为一个脚本,它将 __name__ 变量设置为 __main__。在作为脚本运行的单个 Python 文件中,__name__ 变量的值是 __main__。这就是上面我们以脚本形式运行 other_lib.py 文件时发生的情况。然而,如果该文件被导入,__name__ 的值将会是另一种情况。它将遵循package.path.to.module 的惯例。这里我们有第二个文件,我们只是将 other_lib 模块导入到文件中并运行该文件。另一种方式是,当 Python 导入一个文件时,__name__ 被设置为导入文件的名称。同样重要的是要理解,只要你导入一个文件,Python 就会运行包含在该导入文件中的任何代码。
testing.py
import other_lib
__name__ = other_lib
这个文件是一个脚本吗?
这就是使用 if __name__ == "__main__": main() 模式的最大好处。它允许你检查当前文件是作为一个脚本运行,还是作为一个导入的库运行。如果你看到 __name__ 变量的值是 __main__,那么你就知道它是作为一个脚本运行的。如果 __name__ 变量有任何其他的值,那么我们知道它正在被导入。
为什么要做这个检查?
检查代码是作为脚本运行还是被导入,对代码的使用者有利。如果一个人在阅读你的代码时看到 if __name__ == "__main__": main() 的模式,这就是一个信号,告诉这个人这个文件是一个脚本,他们可以直接运行它。如果一个人在阅读一个 Python 文件时,没有在任何地方看到 if __name__ == "__main__": main() 模式,那么这应该是一个信号,说明你不能,或者至少不应该尝试直接运行这个文件。这应该提示你,这个文件是一个库的一部分,只有当它被导入时才能使用。
这不是由 Python 语言本身强制执行的,但如果你想让你的代码的用户知道你的代码是一个可以运行的脚本,还是一个需要导入的库,这是一个很好的约定。这样就不用猜测程序的入口点在哪里了,if __name__ == "__main__": main() 模式使其明确。
流行的Pycharm IDE能够立即识别这种模式。注意在第5行有一个绿色的箭头,告诉你这是程序的入口点,可以运行。

由于 other_lib.py 文件没有提到 if __name__ == "__main__": main(),Pycharm 很聪明,知道这个文件很可能不是用来直接运行的,而是用来导入的。

PyCharm中默认的main.py脚本
# This is a sample Python script.
# Press Shift+F10 to execute it or replace it with your code.
# Press Double Shift to search everywhere for classes, files,
# tool windows, actions, and settings.
def print_hi(name):
# Use a breakpoint in the code line below to debug your script.
print(f'Hi, {name}') # Press Ctrl+F8 to toggle the breakpoint.
# Press the green button in the gutter to run the script.
if __name__ == '__main__':
print_hi('PyCharm')
提取main()函数
这个模式的一部分涉及到将main()函数提取到自己的函数定义中。你可能会想,为什么我不能把我的代码写在if块里面?你可以,但你可能无意中把变量引入全局范围,这是你想不惜一切代价避免的事情。
Python if __name__ == "__main__": main() Summary
这是一种代码模式,可以保护用户在无意中运行一个脚本。这种模式也被称为有防护的脚本。
当把一个无防护的脚本导入另一个脚本时,第二个脚本会使用第二个脚本的命令行参数来运行第一个脚本,这通常不是有意的。
如果你有一个在无卫兵脚本中写的类,然后把它保存到pickle文件中,那么在另一个脚本中解开pickling将触发对无卫兵脚本的导入,再次产生同样的非预期参数。
为了使你的 Python 代码更安全,并且更容易推理,使用 if __name__ == "__main__": main() 守护脚本模式是有意义的。
