了解 Python 中是否 __name__ == ‘__main__’

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 指南。 祝你学习愉快!🎉