如何理解 if __name__ == "__main__"

很多初学 Python 的同学看到 if __name__ == "__main__" 这段代码都会有疑惑,这段代码是做什么的?似乎没有这段代码程序也可以正常运行。本文我们就来深入讲解一下 if __name__ == "__main__" 到底是什么。

首先 __name__ 是什么?

__name__ 是Python中的一个特殊变量。当Python解释器读取一个代码文件的时候,解释器首先会设置一些特殊变量,其中就包括 __name__ 这个变量,然后解释器执行文件中所有的代码。解释器给 __name__ 赋什么值,取决于我们是把代码文件当作主程序来执行还是把代码文件当作模块 import 进来。当我们把代码文件当作主程序来执行时,__name__ 会被设置成 __main__,而当我们把代码文件作为模块 import 进来的时候,__name__ 会被设置成模块名。

下面我们通过代码来解释上面的两种情况。

代码文件作为主程序执行

第一种情况是把代码文件作为主程序执行。我们把下面的代码存在一个叫 module.py 的文件里

print("before func:", __name__)
def func():
    print("in func")

print("before __name__")
if __name__ == "__main__":
    func()
print("after __name__")

我们在命令行执行

python module.py

会发现它的输出是这样的

before func: __main__
before __name__
in func
after __name__

这个执行的过程中发生了些什么?我们前面提到,Python解释器首先设置变量 __name__。因为我们把代码文件作为主程序来执行,解释器首先会把 __name__ 变量设置成 "__main__",然后一行一行的执行文件中的代码。执行过程如下:

执行第一行代码 print("before func:", __name__) ,打印一行字符串,因为这里 __name__ 已被赋值,所以输出 "before func: __main__"

执行第二行、第三行代码,这里定义了一个函数 func

执行第五行代码 print("before __name__"),打印字符串 "before __name__"

执行第六行代码 if __name__ == "__main__": ,这里比较 __name__ 和字符串 "__main__" 的值,因为两者相等,所以条件判断为真,执行 if 中的语句块 func()func() 是一个函数调用,调用我们前面定义的函数 func,输出 "in func"

最后执行第八行代码 print("after __name__"),打印字符串 "after __name__"

代码文件被当作模块 import 进来

接下来我们再看另外一种情况,module.py 被当做模块import进来的情况。我们新建一个代码文件 main.py,main.py 的内容如下

import module

main.py 是我们的主程序,在 main.py 里我们把 module.py 当作模块 import 进来。

接下来,我们在命令行运行 main.py

python main.py

输出

before func: module
before __name__
after __name__

可以看到这里的输出和前面的输出有些不同。输出里少了一行 in func。这是为什么呢?原因就在于这时我们的 __name__ 发生了变化。 当Python解释器执行 main.py 里的 import module 的时候,它会读入 module.py 这个代码文件。读入代码文件后,它做的第一件事就是设置 __name__ 的值,因为 module 被当作模块 import 进来,所以解释器把 __name__ 设置成了模块的名字 module,然后解释器一行一行地执行文件中的代码。具体的执行步骤如下:

执行第一行代码 print("before func:", __name__), 打印一行字符串,因为这里 __name__ 设成了 "module",所以输出 "before func: module"

执行第二行、第三行代码,这里定义了一个函数 func

执行第五行代码 print("before __name__") ,打印字符串 "before __name__"

执行第六行代码 if __name__ == "__main__":,这里比较 __name__ 和字符串 "__main__" 的值,因为 __name__ 现在是 "module",两者不等,条件判断为假,所以 if 中的语句块不会被执行,也就是 func() 函数不会被调用。

最后执行第八行代码 print("after __name__"),打印字符串 "after __name__"

为什么要使用 if __name__ == "__main__"

当理解了上面的内容后,我们可能要问,为什么要使用 if __name__ == "__main__" 呢? 一个最重要的原因就是,我们需要通过这种方式来保护我们的代码什么时候可以被执行,什么时候不被执行。举个例子,我们在写模块的时候,很多时候会把一些单元测试或者范例代码写在 if __name__ == "__main__" 里面,这些代码我们只希望模块被当做主程序运行时执行,而不希望模块被别的代码 import 时也运行。比如上面的 module.py 里,我们定义了一个叫 func 的函数,然后在 if __name__ == "__main__" 里,我们示范了怎样调用这个函数。因为我们把代码放在了 if __name__ == "__main__" 中,所以这段示范代码只有在我们把 module.py 当做主程序运行时才会执行,而当 module.py 被当作模块 import 进来时不会执行示范代码。

思考题

最后来做一个思考题。把下面的代码保存在 module2.py 里,然后运行它,看看会输出什么?为什么会有这样的输出?

print("begin")
def func():
    print("in func")
    from module2 import func2
    func2()

def func2():
    print("in func2")

if __name__ == "__main__":
    print("before func")
    func()
    print("after func")
print("end")