模板方法模式在python中应优先使用abc.abc + @abstractmethod定义抽象基类,以强制子类实现关键钩子方法,避免运行时才报错;普通父类仅适用于无需强约束的轻量场景。

模板方法模式在Python里用抽象基类还是普通父类?
直接用 abc.ABC + @abstractmethod 是最稳妥的选择。它能强制子类实现关键钩子,避免运行时才报 NotImplementedError。但如果你的项目不追求强约束(比如内部脚本、快速原型),用普通父类+文档注释+类型提示(-> None)也够用,更轻量。
容易踩的坑:
- 误把可选钩子(如
before_execute)也标为@abstractmethod,导致子类被迫实现空方法 - 在抽象方法里调用了未声明为
@abstractmethod的子类方法,IDE 不报错但运行时报AttributeError
如何设计“骨架方法”才能不锁死子类扩展?
核心是把流程拆成原子步骤,并允许跳过或重写任意环节。比如骨架方法 run 应该只负责顺序调用,不包含具体逻辑:
def run(self):
self._validate()
self._prepare()
result = self._execute() # ← 必须由子类实现
self._postprocess(result)
return result
关键点:
- 所有步骤命名带下划线前缀(如
_validate),明确表示“这是受保护的钩子,别直接调用” - 把可选行为做成“空方法体 + 文档说明”,而不是默认抛异常,降低子类负担
- 避免在骨架里做条件分支(如
if self.mode == 'debug': ...),那属于策略模式的职责
为什么 __init__ 里不能调用子类重写的钩子?
因为 Python 中父类 __init__ 执行时,子类实例尚未完全构造,此时调用子类方法可能访问未初始化的属性,引发 AttributeError 或静默错误。
正确做法:
- 把初始化检查移到骨架方法开头(如
run第一行调用self._check_state()) - 或者用延迟初始化:定义
_initialized = False标志,在首次调用关键钩子时检查并初始化 - 绝对不要在
__init__里调用任何以_开头的钩子方法
用 functools.singledispatchmethod 能替代模板方法吗?
不能。单分派适用于“同一方法根据第一个参数类型走不同逻辑”,而模板方法解决的是“固定流程 + 可插拔步骤”。两者粒度和意图完全不同。硬套 singledispatchmethod 会导致骨架逻辑散落在各处,违反开闭原则。
典型混淆场景:
- 想根据输入数据类型选择不同
_execute实现 → 该用策略模式,不是模板方法 - 想让日志格式随环境变化 → 应该作为
_log_result钩子的一部分,而不是 dispatch 分支 - 试图用
@singledispatchmethod注解run→ 会破坏流程控制权,失去“规范执行顺序”的意义
真正难处理的是钩子之间的隐式依赖——比如 _prepare 写了临时文件,_execute 默认读它,但子类重写了 _prepare 却忘了同步改 _execute。这种耦合只能靠文档+测试兜底,代码层面没法全自动约束。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











