Python技术迷

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 → 会打印 3
  • python 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 这种话题,那个又是另外一晚熬夜聊的事情了,就不在这儿展开扯了。