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
结合场景再看三个关注点
- import 不进
__main__分支,所以 Web 层 import 不会误解析sys.argv。 - 顶层适合:import、常量、函数/类定义;不适合:连数据库、读生产密钥。
- 包项目更推荐
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。
边界情况与反直觉行为
- 循环导入:A import B、B import A 时,半初始化模块会导致
AttributeError。解法是推迟导入、重划边界。 - 脚本目录 vs 包目录:直接跑深层文件时,相对导入可能失败;优先模块入口。
python -c与 REPL:也有独立__main__语义,适合临时验证,不适合正式入口。
常见误区
[!warning] 常见误区:把 Python 想成必须有 main 函数 错误理解:没有
def main就不是程序。
正确模型:入口是模块执行 + 可选__main__守卫;def main()只是工程习惯。
工程实践
- 库模块:顶层无副作用;提供清晰函数。
- 应用入口:薄
__main__.py或if __name__,内部调用main()。 - 可测试性:业务函数与 IO/CLI 解析分离。
本节总结
程序结构的第一课是 模块执行模型,不是语法花样。把“可被 import 的库”和“可运行的入口”分开,是后续工程化的地基。
自测题
- 为什么测试文件
import order_id不会执行 CLI 分支? - 顶层
print("loaded")在多次 import 时打印几次?
参考答案
- 因为 import 时
__name__是模块名而非"__main__"。 - 通常一次:首次导入执行并缓存;同进程再次 import 不重跑顶层。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| Tutorial – Modules | 文档 | 模块与 __name__ |
| Reference – Top-level components | 规范 | 程序组成 |