Python中的if __name__ == "__main__": main()

640 阅读5分钟

Python if name== main main

你可能在一些 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行有一个绿色的箭头,告诉你这是程序的入口点,可以运行。

pycharm if def name main

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

pycharm library file

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() 守护脚本模式是有意义的。