正在阅读读懂 Python 程序入口:__name__、help() 与 main()
香港天气加载中…
QWeather ↗
← Back to the notebook

读懂 Python 程序入口:__name__、help() 与 main()

从递归示例末尾的三行代码出发,逐行理解模块身份、函数对象、帮助信息与函数调用,并实际对照直接运行和 import 的不同结果。

01 · 三行代码,分别负责什么

原始问题来自下面这三行代码。先假设 recursion_sum 和 main 已在前面定义或导入。仅凭这三行,不能确定两个函数的具体实现;后文另写一个求和示例来观察行为。

本篇关联递归与调用栈笔记,但主线是 Python 的运行与调用机制。在学习地图中查看关联概念。

Python
if __name__ == '__main__':
    help(recursion_sum)
    main()

02 · __name__:同一个文件的两种运行身份

__name__ 是 Python 自动设置的模块变量,其值是字符串。初学时,可以先把一个普通的 .py 文件理解为一个模块。假设文件名是 recursion_demo.py:

使用方式该文件中的 __name__条件是否成立
python recursion_demo.py'__main__'成立
其他文件执行 import recursion_demo'recursion_demo'不成立
python -m recursion_demo'__main__'成立

'__main__' 标识程序入口所在的模块环境。被导入的模块使用自己的模块名;如果位于包中,模块名还可能是 package.recursion_demo。

所以这行可以读成:“如果当前模块就是这次程序运行的入口,就执行下面缩进的代码。”这里描述的是通常的脚本与导入用法;无需手动给 __name__ 赋值。

写法含义
__name__读取变量的值,没有引号。
'__main__'一个字符串,有引号。
==比较两边是否相等,不是赋值。
: 和缩进确定条件成立时执行哪些语句;本例两条缩进语句都受它控制。

名称两侧各有两个下划线。字符串 '__main__' 与函数名 main 是不同的东西。

03 · help(recursion_sum):查看函数,不执行求和

help() 用来查看帮助信息。把普通函数对象传进去时,它通常会显示函数名称、参数形式和文档字符串。

help(recursion_sum) 中,被调用的是 help;recursion_sum 作为参数传入,没有被调用。“查看它是什么、怎么用”和“让它开始计算”是两件事。

表达式先发生什么后续行为
recursion_sum取得函数对象。不会执行它的函数体。
recursion_sum(3)调用函数。在本文实现中返回整数 6。
help(recursion_sum)把函数对象交给 help。查看这个函数的说明。
help(recursion_sum(3))先调用求和函数,得到 6。再查看整数对象 6 的帮助。

函数在 Python 中也是对象,可以作为参数传给其他函数;这里不需要先调用它。若写成 help(recursion_sum()),本文函数会因为缺少参数 n 而报错,帮助显示也不会开始。

04 · 帮助里的文字从哪里来

完整示例中,函数体的第一条语句是字符串:

"""递归计算 1 + 2 + ... + n;n 必须为非负整数。"""

这叫作文档字符串(docstring),用于说明函数。对本文函数,可通过 recursion_sum.__doc__ 取得这段文字;help() 会把它与函数签名等信息一起整理显示。

三引号表示字符串的一种写法;并不是任意位置的三引号字符串都成为函数文档。函数没有 docstring 时,help() 仍可显示它的名称和参数等信息。

写出输入要求,不等于实现输入校验。本文示例约定 n 为非负整数,且只演示小规模输入,没有加入类型、负数或递归深度保护。更严格的输入契约可参见递归求和笔记。

05 · main():普通函数调用,名字来自约定

main() 就是一次普通的函数调用。main 不是 Python 关键字,也不是一个自动提供的内置函数。它是程序员常用来组织主要流程的名字,必须在调用前已经定义或导入。

下面只是在定义函数,不会执行函数体。执行到 main() 时才进入函数体。若把定义和调用一起改名为 run_demo,程序仍可以工作;入口判断中的字符串依然写成 '__main__'。

Python 不会因为函数叫 main 就自动跳进去执行。这个区别也关联命名约定与运行机制。

Python
def main():
    print("进入 main()")
    print(recursion_sum(3))

06 · 完整文件:recursion_demo.py

将下面代码保存为 recursion_demo.py。顶部的打印用于观察模块身份;两个 def 创建函数,末尾的条件块决定是否运行示例。后面的实际输出来自 Python 3.12.14 的非交互终端。

Python
print(f"当前模块的 __name__ = {__name__}")


def recursion_sum(n):
    """递归计算 1 + 2 + ... + n;n 必须为非负整数。"""
    if n == 0:
        return 0
    return n + recursion_sum(n - 1)


def main():
    print("进入 main()")
    print(recursion_sum(3))


if __name__ == '__main__':
    help(recursion_sum)
    main()

07 · 直接运行:先显示帮助,再计算

在文件所在目录执行:

python recursion_demo.py

实际输出:

当前模块的 __name__ = __main__
Help on function recursion_sum in module __main__:

recursion_sum(n)
    递归计算 1 + 2 + ... + n;n 必须为非负整数。

进入 main()
6

第一行说明当前模块的 __name__ 是 __main__。帮助信息只描述函数,之后的“进入 main()”才标志主要流程开始。真正的求和发生在 main() 内部调用 recursion_sum(3) 时。

08 · Python 实际按什么顺序执行

  1. 执行模块顶部的 print(),显示模块身份。
  2. 执行两个 def,创建并绑定函数对象;尚未执行它们的函数体。
  3. 执行 if 判断;直接运行时条件为 True。
  4. 调用 help(recursion_sum),显示帮助。
  5. 帮助正常返回后,调用 main()。
  6. 在 main() 中调用求和函数,随后打印结果 6。

某些交互终端会用分页器显示帮助,需要退出帮助页后才能继续;帮助内容的具体排版也可能随 Python 版本和终端环境变化。

09 · 换一个文件导入它:consumer.py

在同一目录创建 consumer.py。这个文件导入求和模块,并明确调用其中的函数。

Python
import recursion_demo

print("导入完成")
print(recursion_demo.recursion_sum(3))

10 · 导入时,哪些代码仍然执行

执行:

python consumer.py

实际输出:

当前模块的 __name__ = recursion_demo
导入完成
6

此时,consumer.py 是程序入口;被导入的 recursion_demo.py 使用模块名 recursion_demo。后者的条件为 False,因此不会自动显示帮助或调用 main(),但它创建的函数仍可供调用。

import 会执行模块顶层代码,所以顶部的 print() 仍然出现。条件块只控制自己缩进范围内的语句,不能阻止整个文件的顶层代码执行。

这里展示的是新进程中的首次导入。同一进程中再次执行普通的 import recursion_demo,通常会复用缓存的模块,不会把模块顶层代码重新执行一遍。

11 · 为什么要这样组织文件

同一个文件可以有两个使用目的:独立运行时展示示例;被其他程序或测试导入时提供可复用函数。

代码的位置直接运行首次导入
模块顶层的普通语句执行执行
模块顶层的 def创建函数创建函数
本文函数的函数体调用时执行调用时执行
本例入口判断内的语句执行跳过

如果去掉条件判断,把 help(recursion_sum) 和 main() 直接放在模块顶层,导入模块时也会显示帮助和运行示例。对测试工具而言,这可能意味着仅仅导入一个计算函数,就额外打印内容或启动其他流程。

help(recursion_sum) 是学习时的可选步骤。删掉它,main() 仍能调用;保留入口判断有助于把“提供函数”和“运行演示”分开。

12 · 建议复盘:先预测,再运行

下面是建议练习,不代表你已经提出这些追问、完成操作或掌握概念。

  1. 删除末尾的 main(),直接运行时还会打印 6 吗?
  2. 把 main 的定义和调用一起改名为 run_demo,入口判断中的字符串要跟着改吗?
  3. help(recursion_sum) 与 help(recursion_sum(3)) 分别把什么对象传给帮助系统?
  4. 为什么导入时跳过了条件块,顶部的模块身份仍会打印?
  5. 导入后主动调用 recursion_demo.main(),会执行什么?
展开参考思路

删除 main 调用后,这个示例只显示模块身份和帮助,不计算求和。普通函数可以改名,特殊入口字符串仍是 __main__。两种 help 写法分别接收函数对象和整数 6。首次导入会执行模块顶层语句,但入口条件为假。导入后显式调用 main 仍会执行它的函数体,不会自动执行入口块中的 help。

继续追踪递归求和 · 在学习地图中复盘

13 · 来源与验证范围

本文沿用 2026-10-14 的三行代码提问,说明文字和两文件实验为原创讲解。语言机制以 Python 官方资料为依据:

  1. __main__ — Top-level code environment:入口环境、模块身份与 main 函数惯例。
  2. Built-in Functions · help():对象帮助信息。
  3. Python 教程 · Defining Functions:函数定义、调用与 docstring。
  4. Python 教程 · Modules:模块执行与导入复用。

验证环境:Python 3.12.14。已分别在新进程中运行直接脚本、导入方脚本和 -m 方式,核对输出;另检查 help 不进入本文求和函数体、main 可改名,以及 0、1、3 的求和结果。未运行大规模递归或将 docstring 视为输入校验。

Python__name____main__函数对象help模块导入

先分清模块如何进入执行,再分清你传递的是函数对象,还是一次调用的结果。

MORE IN DEVELOPMENT读懂 SSH 本地端口转发:-N、-L 与两端的 127.0.0.1