Python 中 if _ _name_ _ == '_ _main_ _' 到底是什么意思?
有一天晚上我在公司加班,快十一点了,准备收工,旁边实习生突然问我一句: “哥,这个 if __name__ == "__main__": 到底是干嘛的啊?每个 Python 文件下面都要来一份,看着就烦。”
我当时一愣,发现好多人其实都只是“照抄模板”,根本没搞懂它在干嘛。那就顺着这个问题聊聊。
先说说 __name__ 是谁
Python 每个 .py 文件,其实都可以看成一个“模块”。 Python 在执行一个模块时,会自动给这个模块塞一个内置变量:__name__。
有两个关键场景:
这个文件是“直接运行”的,那么 __name__就会被设置成"__main__"这个文件是“被别的文件 import”的,那么 __name__就会变成“模块名”,比如utils.py就是"utils"
来个特别裸的例子,你新建个 a.py:
print("a.py 里面的 __name__ 是:", __name__)
直接运行:
python a.py
终端会看到:
a.py 里面的 __name__ 是: __main__
好,这时候再新建一个 b.py:
import a
print("b.py 里面的 __name__ 是:", __name__)
运行:
python b.py
这次输出会是类似这样的:
a.py 里面的 __name__ 是: a
b.py 里面的 __name__ 是: __main__
你看出来味道了没? 谁是“入口文件”,谁的 __name__ 就是 "__main__"; 别的被 import 进来的,全都是自己的文件名。
为啥要写那句 if?
现在想象一种很常见的写法:你写了一个工具模块 math_utils.py,里面有函数,也顺手写了点测试代码:
# math_utils.py
defadd(a, b):
return a + b
print(add(1, 2)) # 简单测一下
单独跑 python math_utils.py,没问题,输出 3。
但是某一天,你在 main.py 里:
import math_utils
result = math_utils.add(10, 20)
print("结果是:", result)
这时候你执行 python main.py,会发生啥?
输出是:
3
结果是: 30
你根本没想让 math_utils 打印 3 啊,它只是个工具模块而已,但因为里面有裸奔的 print,只要被 import 一次,就会执行一遍顶层代码。
这就是问题: “我只想在这个文件被当成脚本运行时执行一些逻辑,被 import 的时候别动。”
于是就有了经典写法:
# math_utils.py
defadd(a, b):
return a + b
if __name__ == "__main__":
# 只在直接运行 math_utils.py 时执行
print(add(1, 2))
现在再跑:
python math_utils.py→ 会打印3python main.py(里面 import 了 math_utils) → 不会乱打印3了,只会输出你想要的业务结果
这句 if __name__ == "__main__": 的作用,其实就一句话:把“脚本入口逻辑”和“可复用的模块代码”分开。
一般都怎么配合写?
现实项目里,为了不让文件尾巴那块太乱,我自己一般会再包一层 main 函数:
# main_app.py
defmain():
# 程序真正的入口
print("程序启动")
# 这里写各种业务逻辑,比如读配置、解析参数、调用算法等等
# ...
if __name__ == "__main__":
main()
好处有几个:
逻辑都放在 main()里,结构清爽以后如果要单元测试,直接 from main_app import main就能调被当成库 import 时,不会自动“跑起来”,老老实实提供函数就行
对于你刷算法题也一样,比如写在 solution.py 里:
deftwo_sum(nums, target):
store = {}
for i, x in enumerate(nums):
y = target - x
if y in store:
return [store[y], i]
store[x] = i
if __name__ == "__main__":
# 简单自测
print(two_sum([2, 7, 11, 15], 9))
交到在线评测系统的时候它可能只会 import 你的函数,不会运行最后那几行测试代码,因此加这句 if 很安全。
再补两个稍微进阶一点的点
一个是包里也有“入口脚本”的玩法,比如你有个包:
my_app/
__init__.py
__main__.py
core.py
你可以在 __main__.py 里写:
from .core import run
if __name__ == "__main__":
run()
然后在命令行里可以用这种方式跑:
python -m my_app
这时候 my_app.__main__ 这个模块的 __name__ 也是 "__main__",所以 if 条件成立,程序启动。
另一个点,有时候你会在源码看到这种写法:
if __name__ == "__main__": # pragma: no cover
main()
这是在告诉测试工具(比如 coverage):“别把这一段算进测试覆盖率”,因为它只是启动入口,不是核心逻辑。
别再把那行当“玄学口诀”背了,脑子里就记这么一句:
__name__表示“这个文件现在是怎么被用的”, 如果是入口脚本,就会等于"__main__", 那句 if 就是:只在“直接运行我”的时候才执行下面这坨代码。
搞明白这个,你以后写工具模块、算法库、甚至拆包封模块时,都不会再被那些莫名其妙的输出吓一跳了。
至于数据库选 MySQL 还是 Postgres 这种话题,那个又是另外一晚熬夜聊的事情了,就不在这儿展开扯了。