Python基类方法使用子类变量的两种实现对比及优选方案问询
Python基类调用子类属性的实现方案对比
通用实现逻辑
这种基类依赖子类提供属性的场景属于模板方法模式的典型用法,核心是要明确基类的约定:所有继承Base的子类必须提供sub_resource属性,常见实现有三类:
- 基类构造显式要求传入属性值,即你给出的版本2思路
- 基类提前声明属性占位值,要求子类覆写
- 用抽象属性强制子类实现,从语法层面避免漏写的问题。
两个版本的优缺点
版本1
- 优势:代码更精简,子类不需要额外传参给父类构造
- 劣势:
- 类型检查告警合理:Base类本身确实没有定义
sub_resource属性,如果后续新增子类漏写这个属性,只有运行时才会触发AttributeError,问题暴露晚 - 存在执行顺序隐患:如果后续你在Base的
__init__里调用了用到sub_resource的方法,现在子类是先调super().__init__()再赋值sub_resource,直接就会报错,你现在能跑通只是因为当前父类构造没用到这个属性而已 - 可读性差:其他开发者维护Base类代码时,完全不知道
sub_resource的来源,需要额外翻子类代码或者看文档,维护成本高
- 类型检查告警合理:Base类本身确实没有定义
版本2
- 优势:
- 基类依赖明确:Base的构造函数直接声明需要
sub_resource参数,属性在Base构造阶段就完成初始化,没有未定义的问题,IDE不会告警 - 无执行顺序隐患:不管父类构造里要不要用这个属性,都不会出问题
- 可读性好:看Base的构造就能知道需要传入什么参数,维护成本低
- 基类依赖明确:Base的构造函数直接声明需要
- 劣势:子类代码量稍多,需要提前定义属性再传给父类构造。
推荐方案
绝大多数场景优先选版本2的思路,如果是多人协作的大型项目,更推荐用抽象属性的写法,从语法层面强制子类实现,避免漏写,示例如下:
from abc import ABC, abstractmethod class Base(ABC): def __init__(self): self.base_resource = 'Base.resource' print('Base.__init__') @property @abstractmethod def sub_resource(self) -> str: """子类必须实现的子资源属性""" pass def predict(self): print('Base.predict() start') self.forward() print('Used in Base.predict():', self.base_resource) print('Used in Base.predict():', self.sub_resource) print('Base.predict() end') def forward(self): raise NotImplementedError class A(Base): def __init__(self): super().__init__() self._sub_resource = 'A.sub_resource' print('A.__init__') @property def sub_resource(self): return self._sub_resource def forward(self): print('A.forward() start') print('Used in A.forward():', self.base_resource) print('Used in A.forward():', self.sub_resource) print('A.forward() end')
这种写法类型检查完全合规,要是子类没实现sub_resource,实例化的时候直接就会报错,问题暴露的更早。
内容的提问来源于stack exchange,提问作者mrgloom
相关产品推荐
相关产品推荐

