查看全课大纲(第 36 / 51 节)
- 1.Python 是什么?为什么值得学?
- 2.如何为 Python 编程准备一个“全家桶”环境?
- 3.Python 有哪几种执行方法?
- 4.行与缩进到底有什么用?
- 5.注释有什么用,怎么写?
- 6.变量:如何给数据起个好名字?
- 7.运算符的优先级和常见坑点是什么?
- 8.Python 中,数字和布尔值究竟有哪几种?
- 9.输入函数 `input()` 和文件读取 `open()` 怎么用?
- 10.如何将程序的结果展示给用户或保存下来?
- 11.CSV 文件怎么读怎么写才方便?
- 12.列表是什么,如何创建和访问它?
- 13.列表的修改操作,有哪些方法?
- 14.列表切片为什么能取到部分元素?排序和查找又该怎么用?
- 15.判断语句:如何让程序根据条件做不同的事?
- 16.for 循环如何遍历序列中的元素?
- 17.while 循环什么时候才会停?
- 18.while 循环的结束条件,你写对了吗?
- 19.循环中途想提前结束,怎么办?
- 20.`break` 和 `continue` 到底有什么区别?
- 21.列表推导式如何让代码更简洁高效?
- 22.字符串怎么定义、拼接和操作?
- 23.元组:为什么定义单个元素时要加逗号?
- 24.集合(set)怎么用?如何快速去重和做集合运算?
- 25.字典是什么,为什么比列表更方便?
- 26.字典的增删改查与遍历,如何避免 KeyError?
- 27.函数里的变量,为什么有的能用有的不能用?
- 28.在函数内部修改全局变量,为什么有时不生效?
- 29.函数里能修改全局变量吗?返回值怎么用?
- 30.匿名函数:如何用 lambda 快速定义简单函数?
- 31.面向对象编程是什么,为什么需要它?
- 32.类中的方法和属性到底怎么定义和使用?
- 33.如何控制对对象内部属性和方法的访问?
- 34.异常发生时,程序会怎样继续执行?
- 35.如何组织代码:包和模块
- 36.`if __name__ == '__main__'` 到底有什么用?
- 37.如何用 IDE 的 Debug 功能排查程序错误?
- 38.Matplotlib 绘图的第一步该做什么?
- 39.Figure 是什么,如何设置图形尺寸?
- 40.如何自定义 Matplotlib 的坐标轴?
- 41.如何为图表添加图例,让不同曲线一目了然?
- 42.饼图怎么画才能直观展示数据占比?
- 43.如何用 Matplotlib 绘制散点图?
- 44.柱状图怎么画?
- 45.如何使用 Matplotlib 绘制等高线图?
- 46.如何在 Matplotlib 中绘制子图?
- 47.zip 函数如何把多个列表“打包”在一起?
- 48.如何给可迭代对象加上索引?
- 49.random 模块如何生成随机数?
- 50.math 模块怎么用?
- 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 自己的内容。
边界与注意事项
- 模块顶层代码的执行时机:一个模块中,所有不在函数或类定义内的顶层代码(包括函数调用、
print语句等),在该模块被导入时就会执行一次。这就是为什么在main_test.py中,先输出了module2模块顶层代码的结果。 __name__的值就是模块名:当模块被导入时,__name__是字符串形式的模块名。如果模块在包内,它会包含点号,例如business.module2。- 主要用途是隔离:这个惯用法的核心价值在于隔离。它让你能在一个文件中同时编写可复用的函数(供他人导入)和临时的测试或演示代码(仅供自己直接运行时使用),两者互不干扰。
📝 动手练一练
请你创建一个名为 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 块之外的模块顶层或函数里。 |
小结
__name__是一个内置变量,在模块被直接运行时值为'__main__',被导入时值为模块名。if __name__ == '__main__':是一个惯用法,用于判断当前模块是否正被直接运行。- 其主要用途是将模块的自测试代码或主程序逻辑放入该条件块,确保这些代码仅在文件被直接运行时执行,而在被导入时不执行。
- 可复用的函数、类等定义应放在该条件块之外,以确保它们在任何情况下都能被正确导入和使用。
- 模块顶层的调用和打印语句在模块被导入时也会执行一次,安排代码位置时需要考虑这一点。
- 注意
__name__和__main__的拼写,都是双下划线开头和结尾。
📋 行动清单
- 创建一个简单的
.py文件,分别打印其被直接运行和被导入时的__name__值,验证本节概念。 - 将你之前练习中某个包含测试代码的脚本,用
if __name__ == '__main__':将测试部分包裹起来,使其可以被安全地导入。 - 在
math_utils.py中添加一个减法函数subtract,并在if __name__ == '__main__':块中为它添加测试,然后在另一个文件中导入并使用它。
—— 小象教研组