Menu

Python if __name__ == '__main__' 是什么意思

if __name__ == '__main__' 到底做了什么,为什么每个正经的 Python 脚本最后都会用上这种写法,以及如何让一个文件既能当脚本又能当模块。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

揭秘 Python 中最奇怪的一行代码

打开几乎任何一个 Python 文件,最后你都会在末尾看到这个:

if __name__ == "__main__":
    main()

它看起来像一段神奇的样板代码。但一旦你知道它的作用,就会发现它其实是个非常实用的功能。它之所以存在,是因为 Python 不区分“脚本”和“库”。

前提:一个文件的两种用法

Python 中的每个 .py 文件都有两种不同的用法:

  1. 作为脚本:用 python3 file.py 直接运行。
  2. 作为模块:另一个文件用 import file 导入它。

Python 不要求你声明自己写的是哪一种。任何文件都可能两种用法都有。

来看一个名为 math_helpers.py 的文件:

# math_helpers.py
def mean(values):
    return sum(values) / len(values)

def median(values):
    s = sorted(values)
    mid = len(s) // 2
    if len(s) % 2 == 0:
        return (s[mid - 1] + s[mid]) / 2
    return s[mid]

# Quick smoke test while developing
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
print("mean:", mean(numbers))
print("median:", median(numbers))

如果你直接运行这个文件,它会打印测试输出,很好。但如果其他文件执行了 import math_helpers,这些 print 语句会在导入时运行。每个使用这个模块的人都会看到你开发时的临时输出。这就不好了。

__name__ 是什么

每个 Python 模块都有一个名为 __name__ 的内置变量。Python 会自动设置它:

  • 当文件被导入时,__name__ 被设为模块的导入名,在上面的例子中就是 "math_helpers"。
  • 当文件被直接运行时,Python 把 __name__ 设为特殊字符串 "__main__"。

这样你就能判断当前处于哪种模式。把“只在脚本模式下运行”的代码放进一个判断里:

# math_helpers.py
def mean(values):
    return sum(values) / len(values)

def median(values):
    s = sorted(values)
    mid = len(s) // 2
    if len(s) % 2 == 0:
        return (s[mid - 1] + s[mid]) / 2
    return s[mid]

if __name__ == "__main__":
    numbers = [3, 1, 4, 1, 5, 9, 2, 6]
    print("mean:", mean(numbers))
    print("median:", median(numbers))

现在 python3 math_helpers.py 会打印测试输出。但在其他文件中 import math_helpers 会完全跳过这个代码块:函数照样可用,不想要的东西不会运行。

常见搭档:main() 函数

大多数实际的脚本都会有一个专门的 main() 函数,并让守卫代码块尽量简短:

# process_orders.py
import sys

def load_orders(path):
    with open(path) as f:
        return [line.strip() for line in f if line.strip()]

def process(orders):
    for order in orders:
        print(f"Processing {order}")

def main():
    if len(sys.argv) < 2:
        print("Usage: python3 process_orders.py <file>")
        sys.exit(1)
    orders = load_orders(sys.argv[1])
    process(orders)

if __name__ == "__main__":
    main()

为什么要专门写一个 main(),而不是把逻辑直接放在 if 守卫下面?

  1. 便于测试。你可以导入这个模块,在测试中调用 main()(或任何辅助函数)。
  2. 局部变量。在 main() 中定义的东西都是局部变量,而不是模块级的名字。这能避免意外的命名冲突,也让模块对外暴露的内容更干净。
  3. 可读性。守卫代码块现在只有两行,是一个清晰的“脚本入口”,而不是一堆逻辑。

简单演示

你可以直接看到它的行为:

用 Python 运行上面的代码片段时,它会打印 __name__ is: __main__ 以及包含“being run as a script”的那一行。如果其他文件导入了这个文件,同样的判断会打印另一个分支。

什么时候不需要守卫

很多 Python 文件从来不需要它。没人会导入的简短脚本不写也完全没问题:

# one_off_rename.py
import os
for name in os.listdir("."):
    if name.endswith(".txt"):
        os.rename(name, name.lower())

这样写完全没问题。你并不是在写一个库,也没有人会导入它。但只要文件里出现了可复用的逻辑,而你又想把带副作用的代码和它放在一起,就该用上守卫了。

已安装包的入口点

当你把代码发布成真正的包时,还有一种更正式的机制:包元数据中的 entry_points,它把一个命令映射到一个函数。安装你的包的用户会得到一个直接调用该函数的 shell 命令。不过,对于在你自己电脑上某个文件夹里运行的脚本,__main__ 守卫就是标准做法。

本章小结

你现在已经掌握了:

  • 用函数给逻辑命名。
  • 用 *args/**kwargs 实现灵活的调用。
  • 用 lambda 写一次性函数。
  • 用装饰器和类型提示来包装和说明这些函数。
  • 用模块、pip 和虚拟环境把代码拆分到多个文件和项目中。
  • 用 __main__ 守卫让脚本和模块共存。

下一步:类

到目前为止,你主要是把函数和普通数据(字典、列表、元组)搭配使用。下一章介绍类,这是 Python 把数据和行为绑定在一起的方式,self、__init__、继承和 dataclass 这些概念都在那里。

常见问题

if __name__ == '__main__' 有什么作用?

只有当文件被直接执行时(而不是作为模块被导入时),它下面的代码块才会运行。这样你写的 .py 文件既可以当作脚本运行,也可以被导入来使用其中的函数和类,而导入时不会执行脚本里的操作。

Python 需要 main 函数吗?

不需要。Python 文件会自动从上到下执行。但只要文件里有你希望也能被导入的逻辑,if __name__ == '__main__' 守卫就变得很重要。把脚本的顶层工作放进一个 main() 函数里,是与之搭配的常见习惯。

__name__ 和 __main__ 有什么区别?

__name__ 是 Python 在每个模块中设置的特殊变量。如果文件是被导入的,__name__ 就是模块名(比如 'util')。如果文件是被直接运行的,Python 会把 __name__ 设为字符串 '__main__'。if 判断就是在比较这两者。

Coddy 编程语言插图

和 Coddy 一起学编程

开始