Python 程序结构

Python 以模块为基本编译单元:语句顺序执行、导入绑定名字,以及用 __name__ 区分库与入口脚本。

#type / concept #status / growing #tech / dev #resource / python #tech / lang / python

[!info] 关联笔记

Python 程序结构

这个概念为什么出现

很多语言有 main 函数硬入口。Python 更常是:一个 .py 文件就是一个模块,从上到下执行语句;当你“运行脚本”时,这个模块的名字被设为 "__main__"。不理解这一点,会出现:

  • 写库时顶层代码乱执行
  • 循环导入时初始化顺序失控
  • 测试时无法 import 业务函数

[!abstract] 一句话理解 Python 程序由模块组成:导入执行一次并把结果放进 sys.modules,直接运行时模块名是 __main__,因此用 if __name__ == "__main__": 隔离入口副作用。

最小可运行示例

先把示例放进业务场景,再看代码:

场景:订单号工具既要被 Web 层 import,也能命令行自测

仓储系统需要 normalize_order_id:去掉空格、转大写。Web handler 会 import 它;运维也想 python order_id.py ORD-1 快速试。

# order_id.py
# 业务意图:提供可复用的订单号规范化,并保留 CLI 自测入口。
# 教学点:
# - 模块导入时会执行顶层 def(绑定函数对象),但不应有“打印/连库”副作用;
# - 仅当 __name__ == "__main__" 时解析命令行并打印。

import sys


def normalize_order_id(raw: str) -> str:
    # 业务规则:首尾空白去掉,统一大写,保证检索键一致。
    return raw.strip().upper()


if __name__ == "__main__":
    # 只有“python order_id.py ...”直接运行才走这里。
    if len(sys.argv) != 2:
        print("usage: python order_id.py <order-id>", file=sys.stderr)
        raise SystemExit(2)
    print(normalize_order_id(sys.argv[1]))

建议运行:

python order_id.py "  ab-100  "
python -c "import order_id; print(order_id.normalize_order_id('x-1'))"

期望输出:

AB-100
X-1

结合场景再看三个关注点

  1. import 不进 __main__ 分支,所以 Web 层 import 不会误解析 sys.argv
  2. 顶层适合:import、常量、函数/类定义;不适合:连数据库、读生产密钥。
  3. 包项目更推荐 python -m package.module 作为入口约定。

核心概念与准确模型

1. 模块是基本单位

  • 每个文件对应一个模块对象。
  • 首次 import 时执行模块体,随后缓存;再次 import 通常不重跑顶层。

2. 名字空间

  • 模块有自己的全局名字空间。
  • import order_id 在当前模块绑定名字 order_id 指向该模块对象。
  • from order_id import normalize_order_id 绑定的是函数对象本身。

3. __name__ 规则

加载方式典型 __name__
python order_id.py"__main__"
import order_id"order_id"
python -m order_id"__main__"(以模块方式作入口)

4. 包(package)预告

__init__.py(正规包)或命名空间包的目录可组成包;深入见阶段五导入笔记。阶段一只需要:import package.module

边界情况与反直觉行为

  1. 循环导入:A import B、B import A 时,半初始化模块会导致 AttributeError。解法是推迟导入、重划边界。
  2. 脚本目录 vs 包目录:直接跑深层文件时,相对导入可能失败;优先模块入口。
  3. python -c 与 REPL:也有独立 __main__ 语义,适合临时验证,不适合正式入口。

常见误区

[!warning] 常见误区:把 Python 想成必须有 main 函数 错误理解:没有 def main 就不是程序。
正确模型:入口是模块执行 + 可选 __main__ 守卫;def main() 只是工程习惯。

工程实践

  • 库模块:顶层无副作用;提供清晰函数。
  • 应用入口:薄 __main__.pyif __name__,内部调用 main()
  • 可测试性:业务函数与 IO/CLI 解析分离。

本节总结

程序结构的第一课是 模块执行模型,不是语法花样。把“可被 import 的库”和“可运行的入口”分开,是后续工程化的地基。

自测题

  1. 为什么测试文件 import order_id 不会执行 CLI 分支?
  2. 顶层 print("loaded") 在多次 import 时打印几次?
参考答案
  1. 因为 import 时 __name__ 是模块名而非 "__main__"
  2. 通常一次:首次导入执行并缓存;同进程再次 import 不重跑顶层。

延伸阅读与资料来源

资料类型支撑内容
Tutorial – Modules文档模块与 __name__
Reference – Top-level components规范程序组成
创建于 2026/7/15 更新于 2026/7/15