← [ BACK_TO_NOTES ]

Python 程序结构

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

#concept #growing #dev #python #python

[!info] related notes

Python 程序结构

这个概念为什么出现

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

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

[!abstract] 一句话理解 Python 程序由模块组成:普通导入复用 sys.modules 中的模块;首次加载时先缓存模块对象,再执行模块体。脚本或 -m 入口以 __main__ 运行,可用名称守卫隔离 CLI 行为。

最小可运行示例

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

场景:订单号工具既要被 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 -m order_id 作为入口运行时都会走这里。
    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. 模块是基本单位

  • .py 是常见模块来源,也有内建模块、扩展模块和 namespace package;同一文件以不同模块名加载,可能产生不同对象。
  • 普通首次导入先缓存模块对象再执行模块体;同名再次导入通常复用缓存。reload、删缓存或另一个模块名不属于“只执行一次”的保证。

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. 循环导入可能访问半初始化模块,导致 AttributeError 或 ImportError;循环本身并非必然失败,关键是何时读取尚未绑定的名字。
  2. 脚本目录 vs 包目录:直接跑深层文件时,相对导入可能失败;优先模块入口。
  3. python -c 与 REPL:也有独立 __main__ 语义,适合临时验证,不适合正式入口。

常见误区

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

工程实践

  • 库模块避免启动服务、连接数据库等顶层操作;def 的默认参数和装饰器、class 的类体仍会在定义时执行,__main__ 守卫只保护放在该分支内的代码。
  • 应用入口:薄 __main__.py 或 if __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/10/6
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本