__name__ 与程序入口
同一个 Python 文件既可能被其他文件导入,也可能作为脚本直接运行。两种启动方式都会执行模块的顶层代码,但程序通常需要区分它们:被导入时只提供功能,直接运行时才启动测试或业务流程。
如何判断当前文件的启动方式,并把只应执行一次的代码放在正确位置?Python 通过模块的特殊变量 __name__ 提供判断依据。
案例:区分模块导入与直接运行
需求: 让 tools.py 既能被 main.py 导入,也能单独运行并执行简单测试,同时观察两种启动方式下 __name__ 的值。
功能分析: 先在工具模块中定义可复用函数,再用入口判断包裹测试代码;随后分别直接运行工具模块和运行入口文件,对比输出结果。
创建下面两个文件:
01-direct-and-imported/
├── main.py
└── tools.py在 tools.py 中编写:
print(f"tools.py 中的 __name__:{__name__}")
def add_numbers(left, right):
return left + right
if __name__ == "__main__":
print("正在直接运行 tools.py")
print(add_numbers(10, 20))直接运行 tools.py:
python3 tools.py运行结果:
tools.py 中的 __name__:__main__
正在直接运行 tools.py
30当一个文件作为程序入口直接运行时,它的 __name__ 等于字符串 "__main__"。
接着在 main.py 中导入 tools:
import tools
print(f"main.py 中的 __name__:{__name__}")
print(tools.add_numbers(1, 2))运行入口文件:
python3 main.py运行结果:
tools.py 中的 __name__:tools
main.py 中的 __name__:__main__
3这次 tools.py 是被导入的模块,所以其中的 __name__ 是模块名 "tools";直接运行的 main.py 仍然是程序入口,因此它的 __name__ 是 "__main__"。
入口判断机制
案例中的 if __name__ == "__main__": 用来区分文件是被直接运行,还是作为模块被导入。
下面这段结构通常称为入口判断:
if __name__ == "__main__":
print("只在直接运行当前文件时执行")它的执行规则是:
| 当前文件的使用方式 | __name__ 的值 | 判断内的代码 |
|---|---|---|
| 直接运行 | "__main__" | 执行 |
| 被其他模块导入 | 模块名 | 不执行 |
需要注意,入口判断只会保护缩进在判断内部的代码。tools.py 顶层的下面这行代码仍然会在导入时执行:
print(f"tools.py 中的 __name__:{__name__}")如果某段代码在导入时不应该运行,就要把它放进函数或入口判断中。
程序入口组织
当入口文件包含多步业务流程时,可以把流程集中到 main(),再由入口判断负责启动。
入口文件可以把主要流程放进 main() 函数,再通过入口判断启动:
import tools
def main():
print(f"main.py 中的 __name__:{__name__}")
result = tools.add_numbers(1, 2)
print(f"计算结果:{result}")
if __name__ == "__main__":
main()这种结构把“定义程序流程”和“启动程序”分开。以后其他模块即使导入 main.py,也不会自动执行 main() 中的完整流程。
入口判断不是语法规定的强制写法。对于只有几行代码、也不会被导入的小脚本,可以直接顺序执行;当文件既可能独立运行,又可能被其他模块复用时,再使用入口判断更合适。
模块内快速测试
功能模块如何保留可复用函数,同时提供一个不影响导入过程的快速检查入口?可以把少量测试代码放进入口判断。
功能模块也可以利用入口判断保留简单测试:
def add_numbers(left, right):
return left + right
if __name__ == "__main__":
print(add_numbers(10, 20))直接运行这个文件时可以快速检查函数结果;其他文件导入 add_numbers 时,测试代码不会执行。
这种方式适合学习阶段和少量临时检查。项目中的系统化测试通常会放在独立测试文件中,不需要把大量测试逻辑堆进功能模块。
小结
- 文件直接运行时,
__name__的值是"__main__"。 - 文件被导入时,
__name__通常是它的模块名或带包路径的模块名。 if __name__ == "__main__":可以区分直接运行和被导入两种情况。- 入口判断只保护缩进在其中的代码,其他顶层语句仍会在导入时执行。
- 可以使用
main()函数集中组织程序流程,再通过入口判断启动。
练习
- 直接运行
tools.py,再通过main.py导入它,记录两次__name__的值。 - 在入口判断内部和外部分别添加输出语句,观察导入模块时哪些语句会执行。
- 为上一章的学生系统增加
main()函数,并使用入口判断启动程序。 - 在一个功能模块中编写简单测试,确认测试代码不会在模块被导入时运行。