查看全课大纲(第 36 / 51 节)
  1. 1.Python 是什么?为什么值得学?
  2. 2.如何为 Python 编程准备一个“全家桶”环境?
  3. 3.Python 有哪几种执行方法?
  4. 4.行与缩进到底有什么用?
  5. 5.注释有什么用,怎么写?
  6. 6.变量:如何给数据起个好名字?
  7. 7.运算符的优先级和常见坑点是什么?
  8. 8.Python 中,数字和布尔值究竟有哪几种?
  9. 9.输入函数 `input()` 和文件读取 `open()` 怎么用?
  10. 10.如何将程序的结果展示给用户或保存下来?
  11. 11.CSV 文件怎么读怎么写才方便?
  12. 12.列表是什么,如何创建和访问它?
  13. 13.列表的修改操作,有哪些方法?
  14. 14.列表切片为什么能取到部分元素?排序和查找又该怎么用?
  15. 15.判断语句:如何让程序根据条件做不同的事?
  16. 16.for 循环如何遍历序列中的元素?
  17. 17.while 循环什么时候才会停?
  18. 18.while 循环的结束条件,你写对了吗?
  19. 19.循环中途想提前结束,怎么办?
  20. 20.`break` 和 `continue` 到底有什么区别?
  21. 21.列表推导式如何让代码更简洁高效?
  22. 22.字符串怎么定义、拼接和操作?
  23. 23.元组:为什么定义单个元素时要加逗号?
  24. 24.集合(set)怎么用?如何快速去重和做集合运算?
  25. 25.字典是什么,为什么比列表更方便?
  26. 26.字典的增删改查与遍历,如何避免 KeyError?
  27. 27.函数里的变量,为什么有的能用有的不能用?
  28. 28.在函数内部修改全局变量,为什么有时不生效?
  29. 29.函数里能修改全局变量吗?返回值怎么用?
  30. 30.匿名函数:如何用 lambda 快速定义简单函数?
  31. 31.面向对象编程是什么,为什么需要它?
  32. 32.类中的方法和属性到底怎么定义和使用?
  33. 33.如何控制对对象内部属性和方法的访问?
  34. 34.异常发生时,程序会怎样继续执行?
  35. 35.如何组织代码:包和模块
  36. 36.`if __name__ == '__main__'` 到底有什么用?
  37. 37.如何用 IDE 的 Debug 功能排查程序错误?
  38. 38.Matplotlib 绘图的第一步该做什么?
  39. 39.Figure 是什么,如何设置图形尺寸?
  40. 40.如何自定义 Matplotlib 的坐标轴?
  41. 41.如何为图表添加图例,让不同曲线一目了然?
  42. 42.饼图怎么画才能直观展示数据占比?
  43. 43.如何用 Matplotlib 绘制散点图?
  44. 44.柱状图怎么画?
  45. 45.如何使用 Matplotlib 绘制等高线图?
  46. 46.如何在 Matplotlib 中绘制子图?
  47. 47.zip 函数如何把多个列表“打包”在一起?
  48. 48.如何给可迭代对象加上索引?
  49. 49.random 模块如何生成随机数?
  50. 50.math 模块怎么用?
  51. 51.Counter 如何快速统计元素出现次数?

`if __name__ == '__main__'` 到底有什么用?

约 6 分钟

if __name__ == '__main__' 到底有什么用?

小象实战讲义 · Python 编程从入门到实践

在编写 Python 脚本时,你可能会看到 if __name__ == '__main__': 这样一行代码。它像一个“开关”,决定了文件中的某段代码是在文件被直接运行时执行,还是在被其他文件导入时不执行。理解这个机制,能帮助你更好地组织代码,分离模块功能和自测试逻辑。本节是《Python高级知识》章节中关于模块化编程的重要一环。

💡 核心导读

  • __name__ 是什么:一个内置变量,在不同执行场景下代表不同的值。
  • __main__ 的含义:当 Python 文件被直接运行时,其 __name__ 变量的值就是 '__main__'。
  • 惯用法的用途:将模块的自测试代码或主程序逻辑放入 if __name__ == '__main__': 块中,使其仅在直接运行时生效。
  • 典型场景:编写可复用的模块时,用此方式隔离测试代码,避免模块被导入时意外执行。

__name__ 变量与 __main__

在 Python 中,每个模块(一个 .py 文件)都有一个内置的 __name__ 属性。这个属性的值取决于你如何使用这个文件:

  • 当文件被直接运行时(例如在命令行执行 python module.py),该文件的 __name__ 被设置为字符串 '__main__'。
  • 当文件被作为模块导入时(例如在其他文件中写 import module),该文件的 __name__ 被设置为其模块名(通常是文件名去掉 .py 后缀)。

我们可以通过一个简单的例子来验证:当前正在运行的脚本,__name__ 是 '__main__';而 import 进来的模块,__name__ 就是它自己的模块名。

import math

print("当前脚本的 __name__ =", __name__)
print("被导入的 math 模块 __name__ =", math.__name__)
print("两者是否相等:", __name__ == math.__name__)
当前脚本的 __name__ = __main__
被导入的 math 模块 __name__ = math
两者是否相等: False

如果把判断逻辑写进一个模块文件里,就能区分这两种情况。例如 demo_module.py:

# demo_module.py
print("这个文件的 __name__ 是:", __name__)

if __name__ == "__main__":
    print("这段代码只在文件被直接运行时执行")
else:
    print("这段代码只在文件被作为模块导入时执行")

直接运行 python demo_module.py 时,输出为:

这个文件的 __name__ 是: __main__
这段代码只在文件被直接运行时执行

而在另一个文件中写 import demo_module 时,输出为:

这个文件的 __name__ 是: demo_module
这段代码只在文件被作为模块导入时执行

可以看到,同一个文件 demo_module.py,在被直接运行和被导入时,__name__ 的值是不同的,因此 if-else 分支的执行结果也不同。

if __name__ == '__main__': 的典型用法

这个惯用法最常见的用途是:将模块的测试代码或主程序入口放在 if __name__ == '__main__': 块内。

假设我们编写了一个提供工具函数的模块 business/module2.py:

# business/module2.py
def project_info(source):
    print("信息来自:", source)
    print("project info 被调用")

# 只有直接运行本文件时,才执行下面的自测试代码
if __name__ == "__main__":
    print("This is main block just for test")
    project_info("main")

# 模块顶层代码:被导入时也会执行
project_info("module2")

场景一:直接运行 module2.py 进行测试

此时 __name__ 为 '__main__',if 块内的测试代码会执行;if 块之后的模块顶层代码同样会执行。输出为:

This is main block just for test
信息来自: main
project info 被调用
信息来自: module2
project info 被调用

场景二:在其他文件中导入 module2 模块使用其功能

现在,我们在另一个文件 main_test.py 中导入并使用这个模块:

# main_test.py
import business.module2 as bm

print("--- 在 main_test.py 中调用导入的函数 ---")
bm.project_info("test")

运行 main_test.py,输出如下:

信息来自: module2
project info 被调用
--- 在 main_test.py 中调用导入的函数 ---
信息来自: test
project info 被调用

关键点在于:module2.py 中 if __name__ == '__main__': 块内的测试代码(print("This is main block just for test") 和 project_info("main"))并没有被执行。因为 module2.py 是被导入的,其 __name__ 是 'module2',不等于 '__main__'。

而模块顶层的 project_info("module2") 调用,则会在模块被导入时自动执行一次——这就是为什么先输出了它,再输出 main_test.py 自己的内容。

边界与注意事项

  1. 模块顶层代码的执行时机:一个模块中,所有不在函数或类定义内的顶层代码(包括函数调用、print 语句等),在该模块被导入时就会执行一次。这就是为什么在 main_test.py 中,先输出了 module2 模块顶层代码的结果。
  2. __name__ 的值就是模块名:当模块被导入时,__name__ 是字符串形式的模块名。如果模块在包内,它会包含点号,例如 business.module2。
  3. 主要用途是隔离:这个惯用法的核心价值在于隔离。它让你能在一个文件中同时编写可复用的函数(供他人导入)和临时的测试或演示代码(仅供自己直接运行时使用),两者互不干扰。

📝 动手练一练

请你创建一个名为 math_utils.py 的文件,在其中定义一个计算两数之和的函数 add(a, b)。然后,使用 if __name__ == '__main__': 为该函数添加一段自测试代码,测试 add(5, 3) 是否等于 8。最后,创建另一个文件 use_math.py,导入并使用 math_utils 模块计算 10 + 20,观察自测试代码是否会被执行。

👉 点击查看参考答案

思路:在 math_utils.py 中,将自测试逻辑放入 if __name__ == '__main__': 块。在 use_math.py 中导入模块并调用函数,自测试代码不应执行。

math_utils.py 文件内容:

def add(a, b):
    return a + b

if __name__ == "__main__":
    result = add(5, 3)
    print("测试 add 函数:5 + 3 = {}".format(result))
    if result == 8:
        print("测试通过。")
    else:
        print("测试失败!")
    print("自测试完成。")

use_math.py 文件内容:

import math_utils

sum_result = math_utils.add(10, 20)
print("使用 math_utils 模块计算:10 + 20 = {}".format(sum_result))

运行 python math_utils.py,__name__ 是 '__main__',自测试会执行:

测试 add 函数:5 + 3 = 8
测试通过。
自测试完成。

运行 python use_math.py,math_utils 是被导入的模块,__name__ 是 'math_utils',自测试不会执行,只会输出:

使用 math_utils 模块计算:10 + 20 = 30

常见错误

错误写法现象或报错原因正确写法
将主逻辑全放在 if __name__ == '__main__': 块内,模块被导入后无可用功能导入模块后,发现其中定义的函数或变量无法访问(NameError)可复用的函数、类定义必须放在 if 块之外的模块顶层,否则它们只在该文件被直接运行时才被定义。将函数/类定义提升到 if 块之外。if 块内只放调用它们的测试代码。
误写成 if __name__ == '_main_':(单下划线)或 if _name_ == '__main__':条件永远不成立,if 块内的代码永远不会执行(静默失败)。__name__ 和 __main__ 都是前后各两个下划线。拼写错误导致条件永远为假。仔细检查拼写:if __name__ == '__main__':
把 if __name__ == '__main__': 写在被导入的模块里,却指望导入方执行它导入方运行时,if 块内的代码没有执行。被导入时该模块的 __name__ 是模块名,不等于 '__main__',条件不成立。需要被导入方执行的逻辑,直接写在 if 块之外的模块顶层或函数里。

小结

  1. __name__ 是一个内置变量,在模块被直接运行时值为 '__main__',被导入时值为模块名。
  2. if __name__ == '__main__': 是一个惯用法,用于判断当前模块是否正被直接运行。
  3. 其主要用途是将模块的自测试代码或主程序逻辑放入该条件块,确保这些代码仅在文件被直接运行时执行,而在被导入时不执行。
  4. 可复用的函数、类等定义应放在该条件块之外,以确保它们在任何情况下都能被正确导入和使用。
  5. 模块顶层的调用和打印语句在模块被导入时也会执行一次,安排代码位置时需要考虑这一点。
  6. 注意 __name__ 和 __main__ 的拼写,都是双下划线开头和结尾。

📋 行动清单

  • 创建一个简单的 .py 文件,分别打印其被直接运行和被导入时的 __name__ 值,验证本节概念。
  • 将你之前练习中某个包含测试代码的脚本,用 if __name__ == '__main__': 将测试部分包裹起来,使其可以被安全地导入。
  • 在 math_utils.py 中添加一个减法函数 subtract,并在 if __name__ == '__main__': 块中为它添加测试,然后在另一个文件中导入并使用它。

—— 小象教研组