读懂 Python 程序入口:__name__、help() 与 main()
从递归示例末尾的三行代码出发,逐行理解模块身份、函数对象、帮助信息与函数调用,并实际对照直接运行和 import 的不同结果。
01 · 三行代码,分别负责什么
原始问题来自下面这三行代码。先假设 recursion_sum 和 main 已在前面定义或导入。仅凭这三行,不能确定两个函数的具体实现;后文另写一个求和示例来观察行为。
本篇关联递归与调用栈笔记,但主线是 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 就自动跳进去执行。这个区别也关联命名约定与运行机制。
def main():
print("进入 main()")
print(recursion_sum(3))06 · 完整文件:recursion_demo.py
将下面代码保存为 recursion_demo.py。顶部的打印用于观察模块身份;两个 def 创建函数,末尾的条件块决定是否运行示例。后面的实际输出来自 Python 3.12.14 的非交互终端。
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 实际按什么顺序执行
- 执行模块顶部的
print(),显示模块身份。 - 执行两个
def,创建并绑定函数对象;尚未执行它们的函数体。 - 执行
if判断;直接运行时条件为True。 - 调用
help(recursion_sum),显示帮助。 - 帮助正常返回后,调用
main()。 - 在
main()中调用求和函数,随后打印结果6。
某些交互终端会用分页器显示帮助,需要退出帮助页后才能继续;帮助内容的具体排版也可能随 Python 版本和终端环境变化。
09 · 换一个文件导入它:consumer.py
在同一目录创建 consumer.py。这个文件导入求和模块,并明确调用其中的函数。
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 · 建议复盘:先预测,再运行
下面是建议练习,不代表你已经提出这些追问、完成操作或掌握概念。
- 删除末尾的
main(),直接运行时还会打印6吗? - 把
main的定义和调用一起改名为run_demo,入口判断中的字符串要跟着改吗? help(recursion_sum)与help(recursion_sum(3))分别把什么对象传给帮助系统?- 为什么导入时跳过了条件块,顶部的模块身份仍会打印?
- 导入后主动调用
recursion_demo.main(),会执行什么?
展开参考思路
删除 main 调用后,这个示例只显示模块身份和帮助,不计算求和。普通函数可以改名,特殊入口字符串仍是 __main__。两种 help 写法分别接收函数对象和整数 6。首次导入会执行模块顶层语句,但入口条件为假。导入后显式调用 main 仍会执行它的函数体,不会自动执行入口块中的 help。
13 · 来源与验证范围
本文沿用 2026-10-14 的三行代码提问,说明文字和两文件实验为原创讲解。语言机制以 Python 官方资料为依据:
- __main__ — Top-level code environment:入口环境、模块身份与 main 函数惯例。
- Built-in Functions · help():对象帮助信息。
- Python 教程 · Defining Functions:函数定义、调用与 docstring。
- Python 教程 · Modules:模块执行与导入复用。
验证环境:Python 3.12.14。已分别在新进程中运行直接脚本、导入方脚本和 -m 方式,核对输出;另检查 help 不进入本文求和函数体、main 可改名,以及 0、1、3 的求和结果。未运行大规模递归或将 docstring 视为输入校验。