Python_程序入口与__name__约定详解_脚本与模块双模式与 C/C/Java 等常见「main为固定入口」不同Python 语言不在语法上强制定义一个名为main的函数程序从文件顶层开始执行。实践中通过内置变量__name__与约定if __name__ __main__:区分脚本直跑与被 import从而兼顾可复用与可执行。本文按执行模型说明原理并给出惯用写法、易错点与扩展-m、包内__main__.py。叙述以 CPython 常见行为为默认细节以所用版本的官方文档为准。目录1. 为什么常说 Python「没有强制的 main」2. 解释器视角自顶向下与模块对象3.__name__的两种常见取值4. 惯用法if __name__ __main__:5. 含import的一次执行过程直观6. 与python -m及包内__main__.py6.1python -m与runpy的执行机制6.2 包内__main__.py的写法规矩7. 易错点与反模式8. 常见问题简表9. 延伸阅读与免责声明1. 为什么常说 Python「没有强制的 main」对比维度常见 C/C/Java 直觉Python脚本/普通模块入口有固定main或类似作为约定入口符号无必须在源码里写def main的语法入口由你敲的命令行与被加载的文件决定首批执行经编译/类加载后再从入口进解释器加载模块后从文件第 1 行起跑模块级代码定义函数/类是「注册」不立刻跑函数体复用可链接成库同一.py既可被 import当库也可被直接跑当工具/脚本见第 34 节一句话没有「没写main就编译/链接不过」的墙入口解释器要执行的那份顶层的__name__会告诉你当前模块扮演什么角色。2. 解释器视角自顶向下与模块对象执行某个.py时解释器会创建模块对象并逐条执行顶层语句import、def、class、裸print/x1等均在此列。def/class仅定义对象函数体/类体内部语句要等调用或实例化才跑。因此import 会执行子模块的顶层——若把太多「一加载就动刀」的代码铺在最外层会污染import的副作用。第 4 节约定是为限制这类副作用。3.__name__的两种常见取值每个模块有一个__name__属性字符串运行方式__name__典型取值含义心智模型直跑python myapp.py__main__解释器为「主模块」起特殊名避免与包内模块名冲突。importimport mylib或from pkg import mod通常为mylib/pkg.mod等全限定名当前文件在导入图里是一个普通模块。-m包入口python -m mypkg见第 6 节对实际被执行的那段入口代码其__name__仍为__main__对应的是**顶层代码环境top-level code / top-level__main__语境**里的那一份未必等于「磁盘上你随手点的第一个.py路径」。注意术语官方在__main__与包 等文档里把__name__为__main__的环境表述为顶层/主__main__模块的语境。简单情形直接python script.py时该文件就是这份顶层环境python -m pkg时顶层环境对应pkg.__main__子模块__name__仍是__main__不是「整个 import 树里每个文件都叫__main__」。方式: 直接执行本文件__name__ __main__方式: 被 import__name__ 模块全名上图抽象「直跑 / import」双模式。python -m时同样是__name__为__main__的顶层环境但对应文件常为pkg/__main__.py第 6 节。__name__的精确字符串以实际包路径与import写法为准。4. 惯用法if __name__ __main__:目的把仅当本文件作脚本才该执行的逻辑命令行、演示、自测、启动服务器等与提供给别人的 API函数、类分开。常见骨架注意是双下划线__name__下划线不能漏defmain()-None:# 仅本文件为入口时希望执行的流程...if__name____main__:main()写法直接运行本文件被import时顶层print(hi)会执行也会在import时执行除非在if内只在if __name__ __main__:里调用main()会执行不会进入该块main()不自动跑def main不是关键字要求是社区风格关键判断行是if __name__ __main__:。5. 含import的一次执行过程直观场景python main_script.py其内部import my_module且两者都有if __name__ __main__:。my_modulemain_script用户/解释器my_modulemain_script用户/解释器执行其顶层(定义等), __name__ 为 my_module启动, __name__ 为 __main__import my_module模块对象就绪(不跑 my_module 的 main 块)继续 main_script, 可调用 my_module 中符号要点main_script里__name__为__main__my_module里为my_module无包时。在my_module中写在if __name__ __main__:里的自测代码不会在「被main_scriptimport」时执行。6. 与python -m及包内__main__.py除直接python 路径/脚本.py外还常用按模块名运行python -m ...。这与「把某个路径当脚本文件读进来」在机制上不同——由标准库runpy按模块/包命名空间去定位、加载并作为__main__执行详见官方Locating the script与Executing modules的说明。下面分两层展开。6.1python -m与runpy的执行机制本质python -m pkg并不是「在文件系统上随便点一个.py当主文件」的别名而是用模块名在导入系统里找目标并交给runpy走可执行模块协议文档入口。对「包」的常见路径若pkg是包则解释器会去找并执行该包的__main__子模块即pkg.__main__通常对应pkg/__main__.py。在加载/执行前父包往往先被作为包结构载入__init__.py等依项目布局而定不是本文重点细节以官方对 package.main的描述 为准。__name__的取值在runpy这次执行中作为入口跑起来的那段代码其__name__仍会被设为__main__。这与第 3 节「__name__为__main__的顶层执行环境」的表述一致——只是哪个物理文件在扮演这份顶层随-m与包结构而变。和单文件直跑对比python -m不依赖当前工作目录里的裸文件名是否拼对在包内相对导入、分发 CLI 时通常更稳但仍受sys.path、可编辑安装、命名空间包等影响踩坑时优先查官方Using command-line/-m小节。python -m mypkgrunpy: 在导入命名空间中定位可执行目标执行 mypkg.__main__ (常见为 __main__.py)该子模块: name 绑定为 顶层 主 模块 名「主模块名」在 CPython 中即字符串__main__与第 3 节一致。6.2 包内__main__.py的写法规矩点说明角色作为包的CLI/命令行入口python -m pkg时由runpy拉起的模块见 The__main__.pyin packages 的叙述。推荐风格尽量薄在__main__.py里用少量代码组合、调包内其它模块已经写好的main/cli函数避免在入口层堆业务与副作用。这样-m、可安装脚本console_scripts、zipapp等路径行为更一致以项目构建配置为准。if __name__ __main__:要不要写在「仅作为-m的专用入口、几乎从不import pkg.__main__」的极简项目里顶层直接调main()的写法很常见若你还需要在别的测试里import mypkg.__main__且不跑 CLI保留if __name__ __main__:仍有价值。二者不是二极管对立以是否**要兼顾「被当普通子模块 import」**为准。避免误区别误以为「进了__main__.py就自动免学第 34 节」阅读__main__标准库页时应把它与runpy一起看理解「谁在什么语境下是顶层」。与第 4 节单文件的if __name__习惯不矛盾分发给 PyPI/内部仓库时常见组合是库内仍用if __name__可测包入口/控制台脚本走-m或console_scripts后者常涉及下一节的退出码见第 7 节。7. 易错点与反模式问题说明写成if name main:或if __name__ __main__错误需__name__全名与双等号比较。把重逻辑、读配置、建连接全放在模块顶层任何import都触发拖慢/破坏库用户应移入函数或显式main/init。多进程/Windowsmultiprocessing默认会再 import 主模块若主模块直跑大段无保护顶层代码可能递归/重复执行常需if __name__ __main__:内再起子进程详见官方multiprocessing说明。main()返回值 与sys.exit()用setuptools/pip 等装成 CLI 时入口包装里常见sys.exit(main())一类模式具体以打包文档与生成代码为准。main()应返回None或 整数退出码0 表成功。若返回非空字符串等sys.exit会把它当作错误信息并令进程以非零方式结束文档sys.exit——与「我本想返回给调用方看的普通字符串」容易混淆。混淆「入口」与「main名字」main只是函数名习惯语言只认__name__与模块执行方式。8. 常见问题简表问题简短答必须写def main吗不必须可直接在if __name__ __main__:下写语句。写def main为清晰。import会跑对方文件里哪些代码所有模块级未进if/def等的语句。同一文件会既是__main__又被当模块名吗同一进程里一次加载二选一直跑为__main__import为模块名不会同一次既是又是。包内子模块的__name__是什么一般为带点的限定名如pkg.subpython -m pkg时当次顶层的__name__是__main__对pkg.__main__子模块见第 3、6 节。main()该返回什么给sys.exit(main())整数退出码或None避免在打算被包装成sys.exit(main())时返回随意字符串会触发「带信息的异常退出」语义见第 7 节表。9. 延伸阅读与免责声明Python 官方文档Tutorial__name__与脚本模式英文站点侧可选中文镜像或第三方翻译以版权与准确性自行斟酌。Python 标准库runpy— Locating and executing Python modules标准库__main__模块的说明解释__name__为__main__的语境标准库sys.exit与main()返回值/退出码的关系见第 7 节打包侧setuptools文档 中关于entry points/console scripts的当前说明生成包装与sys.exit的具体形式以版本与模板为准免责声明不同 Python 实现CPython、PyPy 等与历史版本在__name__、-m、包布局上可能有边界差异生产配置以当前版本官方文档与项目测试为准。文末再强调一次if __name__ __main__:中必须是__name__两侧各两条下划线否则条件恒不成立或抛语法错误。