Python子类run()方法属性赋值不一致的优化方案咨询
我在子类中实现了run()方法,每个子类的该方法都包含API POST请求,从请求结果中提取ID并赋值给self._id。现在需要获取description字段,但SubClassB的self._api_obj.trigger(...)接口会返回description,而SubClassA的self._api_obj.trigger_run(...)接口不返回该字段,SubClassA需要单独调用API获取description。
当前实现中,SubClassB的run()方法同时赋值_id和_description,而SubClassA的run()仅赋值_id,这种同名方法行为不一致的做法不够优雅,希望找到更优的self._description赋值或获取方案。
原代码如下:
class SuperClass: def __init__(self): self._id = None # Assigned in run() self._description = None @property def description(self): raise NotImplementedError def run(self, *args): raise NotImplementedError @property def id(self): return self._id class SubClassA(SuperClass): def __init__(self): super().__init__() self._api_obj = ApiObj1() @property def description(self): if not self._description: _result = self._api_obj.get_data() self._description = _result["description"] return self._description def run(self, *args): _result = self._api_obj.trigger_run(foo="foo") self._id = _result["RunId"] class SubClassB(SuperClass): def __init__(self): super().__init__() self._api_obj = ApiObj2() @property def description(self): return self._description def run(self, *args): _result = self._api_obj.trigger(foo="foo", bar="bar", arg1="arg1", arg2="arg2") self._id = _result["data"]["id"] self._description = _result["data"]["description"]
方案一:统一run方法职责,description全量懒加载
让run()方法只专注于触发操作并设置_id,_description的获取统一交给description属性懒加载处理,这样两个子类的run()行为完全一致,同时避免不必要的API请求(比如不需要description时就不会触发额外调用)。
修改后的代码:
class SuperClass: def __init__(self): self._id = None # Assigned in run() self._description = None @property def description(self): raise NotImplementedError def run(self, *args): raise NotImplementedError @property def id(self): return self._id class SubClassA(SuperClass): def __init__(self): super().__init__() self._api_obj = ApiObj1() @property def description(self): if not self._description: _result = self._api_obj.get_data() self._description = _result["description"] return self._description def run(self, *args): _result = self._api_obj.trigger_run(foo="foo") self._id = _result["RunId"] class SubClassB(SuperClass): def __init__(self): super().__init__() self._api_obj = ApiObj2() self._trigger_result = None # 存储trigger的结果,用于后续提取description @property def description(self): if not self._description: # 如果trigger已经执行过,直接从存储的结果中取 if self._trigger_result: self._description = self._trigger_result["data"]["description"] else: # 极端情况:未调用run就获取description,可根据需求抛出异常或触发run raise RuntimeError("Please call run() first to get description") return self._description def run(self, *args): self._trigger_result = self._api_obj.trigger(foo="foo", bar="bar", arg1="arg1", arg2="arg2") self._id = self._trigger_result["data"]["id"]
方案二:抽象description获取逻辑,父类定义模板方法
在父类中定义获取description的模板方法,子类各自实现具体的获取逻辑,同时保持run()方法的一致性。
修改后的代码:
class SuperClass: def __init__(self): self._id = None # Assigned in run() self._description = None @property def description(self): if not self._description: self._description = self._fetch_description() return self._description def _fetch_description(self): raise NotImplementedError def run(self, *args): raise NotImplementedError @property def id(self): return self._id class SubClassA(SuperClass): def __init__(self): super().__init__() self._api_obj = ApiObj1() def _fetch_description(self): _result = self._api_obj.get_data() return _result["description"] def run(self, *args): _result = self._api_obj.trigger_run(foo="foo") self._id = _result["RunId"] class SubClassB(SuperClass): def __init__(self): super().__init__() self._api_obj = ApiObj2() self._trigger_result = None def _fetch_description(self): if not self._trigger_result: raise RuntimeError("Please call run() first to get description") return self._trigger_result["data"]["description"] def run(self, *args): self._trigger_result = self._api_obj.trigger(foo="foo", bar="bar", arg1="arg1", arg2="arg2") self._id = self._trigger_result["data"]["id"]
方案三:强制run()完成所有必要数据加载
如果业务上要求run()执行后必须获取到所有后续需要的字段,可以让SubClassA在run()中主动调用获取description的API,这样两个子类的run()都完成_id和_description的赋值,行为完全一致。但要注意这种方式会增加API请求次数,如果不需要description时会造成浪费。
修改后的代码:
class SuperClass: def __init__(self): self._id = None # Assigned in run() self._description = None @property def description(self): return self._description def run(self, *args): raise NotImplementedError @property def id(self): return self._id class SubClassA(SuperClass): def __init__(self): super().__init__() self._api_obj = ApiObj1() def run(self, *args): _result = self._api_obj.trigger_run(foo="foo") self._id = _result["RunId"] # 主动调用API获取description desc_result = self._api_obj.get_data() self._description = desc_result["description"] class SubClassB(SuperClass): def __init__(self): super().__init__() self._api_obj = ApiObj2() def run(self, *args): _result = self._api_obj.trigger(foo="foo", bar="bar", arg1="arg1", arg2="arg2") self._id = _result["data"]["id"] self._description = _result["data"]["description"]
内容的提问来源于stack exchange,提问作者user2653179

