Python 中 `if __name__ == ‘__main__’` 的奥秘
本指南将深入探讨 Python 中 `if __name__ == ‘__main__’` 这一语句的功能及其重要性。
你是否在阅读 Python 代码时,尤其是在涉及多个模块的项目中,经常看到这个条件语句?
如果你是这样,那么接下来我将带你揭开 `if __name__ == ‘__main__’` 的神秘面纱,并通过一个实际例子展示它在开发中的作用。
让我们开始吧!
Python 中 `__name__` 的含义
在 Python 中,模块实际上就是一个 `.py` 文件,它可能包含函数定义、待执行的表达式等等。 例如,如果有一个名为 `hello_world.py` 的文件,我们就可以将其称为 `hello_world.py` 文件或 `hello_world` 模块。
当我们运行 Python 模块时,Python 解释器会在执行前设置一些特殊的变量,`__name__` 就是其中之一。 理解 `__name__` 的关键在于理解 Python 的导入机制。
📁 下载本节的代码。
请移步到 `example-1` 文件夹。 这里有一个 `module1.py` 文件。 `__name__` 变量存在于当前模块的命名空间内。
该模块会先打印一条消息,然后打印 `__name__` 变量的值。
# example-1/module1.py print("这是 module1。") print(f"module1 的 __name__ 变量是: {__name__}。")
现在,让我们从命令行运行 `module1`。
$ python module1.py
输出结果显示,`__name__` 变量的值为 `__main__`。
这是 module1。 module1 的 __name__ 变量是: __main__。
Python 中的模块导入
除了运行 Python 模块之外,我们经常需要在当前模块中使用其他模块的功能。Python 通过导入机制实现了这一点。
导入允许我们复用其他模块的功能,无需重复编写代码。通过将模块导入到当前模块的作用域中,我们就可以直接调用其中的函数和变量。
`module2.py` 文件包含以下内容,其中我们导入了 `module1`。
# example-1/module2.py import module1 # 导入 module1 print(f"这是 module2") print(f"module2 的 __name__ 变量是: {__name__}。")
我们现在运行 `module2.py` 并观察输出。
$ python module2.py
从下面的输出中,我们可以看到:
- 当我们把 `module1` 导入到 `module2` 中时,`module1` 在后台运行,并打印出相应的输出。
- 但是此时,`module1` 中的 `__name__` 变量的值不再是 `__main__`,而是 `module1`。
- 由于我们直接运行了 `module2`,所以 `module2` 中的 `__name__` 变量的值为 `__main__`。
输出结果 这是 module1。 module1 的 __name__ 变量是: module1。 这是 module2 module2 的 __name__ 变量是: __main__。
💡 关键点:
– 如果一个模块被直接运行,它的 `__name__` 变量会被设置为 `__main__`。
– 如果一个模块被导入到其他模块中,它的 `__name__` 变量会被设置为模块自身的名称。
`if __name__ == ‘__main__’` 在 Python 中的应用示例
接下来,我们来看一个 `if __name__ == ‘__main__’` 条件语句的实际应用。 我们会定义一个简单的函数,然后编写单元测试来验证该函数是否按预期工作。
📁 下载代码并跟随。
本节的代码位于 `example-2` 文件夹中。
这里,`add.py` 是一个包含 `add_ab()` 函数定义的 Python 文件。 `add_ab()` 函数接受两个数字并返回它们的和。
# example-2/add.py def add_ab(a,b): return a + b
我们将使用 Python 的 `unittest` 模块来测试 `add_ab()` 函数。
为 Python 函数编写测试用例
请查看以下 `test_add` 模块的内容。
# example-2/test_add.py import unittest from add import add_ab class TestAdd(unittest.TestCase): def test_add_23(self): self.assertEqual(add_ab(2,3), 5) def test_add_19(self): self.assertEqual(add_ab(1,9), 10) def test_add_1_minus7(self): self.assertEqual(add_ab(1,-7), -6)
以上代码执行以下操作:
- 导入 Python 内置的 `unittest` 模块。
- 从 `add` 模块导入 `add_ab()` 函数。
- 定义一个测试类 `TestAdd`,以及一组作为类方法的测试用例。
为了为代码设置单元测试,你需要定义一个继承自 `unittest.TestCase` 的测试类。所有的测试用例都应该以类方法的形式指定,并以 `test_` 开头。
请注意:如果不将方法命名为 `test_
现在让我们尝试从终端运行 `test_add` 模块。
$ python test_add.py
你会发现没有输出,也没有运行任何测试。
为什么会这样?🤔
这是因为,要运行单元测试,你需要在使用以下命令运行 `test_add.py` 时将 `unittest` 作为主模块运行。
$ python -m unittest test_add.py
运行以上命令后,我们看到所有三个测试都成功通过。
输出结果 ... ---------------------------------------------------------------------- Ran 3 tests in 0.000s OK
但如果该模块在被直接运行时能够自动执行测试,那会更加方便,对吧? 让我们在下一节学习如何实现这一点。
使用 `if __name__ == ‘__main__’` 作为主模块运行 `unittest`
如果你希望在模块被直接运行时执行所有单元测试,可以添加以下条件语句。
# example-2/test_add.py import unittest from add import add_ab class TestAdd(unittest.TestCase): def test_add_23(self): self.assertEqual(add_ab(2,3), 5) def test_add_19(self): self.assertEqual(add_ab(1,9), 10) def test_add_1_minus7(self): self.assertEqual(add_ab(1,-7), -6) # 如果作为主模块运行 unittest if __name__ == '__main__': unittest.main()
上面代码片段中的条件语句告诉 Python 解释器:如果这个模块被直接运行,那么就执行里面的代码,即 `unittest.main()`。
在添加以上两行代码后,你就可以直接运行 `test_add` 模块了。
$ python test_add.py
▶️ 现在,直接运行 `test_add` 模块会执行我们定义的所有三个测试。
输出结果 ... ---------------------------------------------------------------------- Ran 3 tests in 0.000s OK
上面的输出结果中的 `OK` 表示所有的测试都成功运行。 三个点 `…` 表示运行了三个测试,并且全部通过。
现在,我们将 `test_add_1_minus7` 的预期返回值改为 `8`。 由于函数返回 `-6`,应该会有一个测试失败。
def test_add_1_minus7(self): self.assertEqual(add_ab(1,-7), 8)
如下面的输出所示,我们得到了 `.F.`,在三个测试中,有一个测试失败(第二个测试),在回溯中,我们得到了一个 `AssertionError` 声明:`-6 != 8`。
输出结果 .F. ====================================================================== FAIL: test_add_1_minus7 (__main__.TestAdd) ---------------------------------------------------------------------- Traceback (most recent call last): File "test_add.py", line 12, in test_add_1_minus7 self.assertEqual(add_ab(1,-7), 8) AssertionError: -6 != 8 ---------------------------------------------------------------------- Ran 3 tests in 0.021s FAILED (failures=1)
值得注意的是,测试并不一定按照它们在测试类中被指定的顺序运行。 在上面的示例中,`test_add_1_minus7` 被定义为测试类中的第三个方法,但对应的测试是第二个运行的。
总结
我希望本教程能够帮助你理解 `if __name__ == ‘__main__’` 条件语句在 Python 中的工作原理。
以下是关键要点的快速回顾:
- Python 解释器在执行 Python 脚本之前设置 `__name__` 变量。
- 当模块被直接运行时,`__name__` 的值为 `__main__`。
- 当你在另一个 Python 脚本中导入模块时,`__name__` 的值是模块的名称。
- 你可以使用 `if __name__ == ‘__main__’` 来控制模块的哪些部分在被直接运行时和被导入时分别执行。
接下来,请查看更多深入的 Python 指南。 祝你学习愉快!🎉