在Python Protocol中定义初始化参数是否合理?求替代方案
方案建议:ABC vs Protocol 以及初始化参数优化
核心差异梳理
- Protocol:仅用于静态类型检查,不提供运行时约束,也无法自动帮你完成实例属性绑定——这正是你现在需要手动编写
self.xxx = xxx的原因,它只要求子类匹配方法签名,不干预内部实现逻辑。 - ABC(抽象基类):支持运行时强制约束,还能封装通用逻辑(比如统一的初始化方法),刚好能解决你重复编写属性绑定代码的痛点。
是否应该用ABC?完全可以,且适配你的场景
如果所有Action子类都需要这三个固定依赖,用ABC抽离初始化逻辑是最优解,能避免每个子类重复写属性绑定代码。示例实现如下:
from abc import ABC, abstractmethod class Action(ABC): def __init__(self, element_finder: ElementFinder, night_club: NightClub, pharmacy: Pharmacy): # 统一绑定所有依赖到实例,子类无需重复编写 self.element_finder = element_finder self.night_club = night_club self.pharmacy = pharmacy @abstractmethod def do_action(self): pass @abstractmethod def restore(self): pass
子类只需实现抽象方法,初始化逻辑直接继承父类:
class ConcreteAction(Action): def do_action(self): # 直接使用self.element_finder等属性即可 pass def restore(self): pass
额外优化思路
如果后续依赖可能变动,或不想把依赖硬编码在ABC初始化中,还可以考虑:
- 依赖注入工具:比如使用
inject库,将依赖获取逻辑与业务代码解耦,子类无需关注初始化参数,直接通过装饰器获取依赖。 - kwargs传递:若依赖数量不确定,可将初始化参数改为
**kwargs,在ABC中统一提取所需依赖,但这会损失类型提示的优势,不推荐在类型严格的场景使用。
总结
如果所有Action子类的依赖固定,优先选择ABC——既能统一初始化逻辑、减少重复代码,又能在运行时强制子类实现必要方法;若仅需静态类型检查、不需要运行时约束,Protocol也可保留,但需接受手动绑定属性的重复工作。
内容的提问来源于stack exchange,提问作者kutasek69
相关产品推荐
相关产品推荐

