Python显式方法签名与LSP冲突:额外参数处理方案咨询
面向对象编程(Python)设计问题咨询
我想咨询一个面向对象编程(OOP)的设计问题,代码基于Python编写。这段代码不符合类型规则(尤其是里氏替换原则LSP),尽管Python允许运行,但我重视设计原则,希望优化代码。
不符合类型规则的代码示例
from abc import ABC, abstractmethod class Model(ABC): @abstractmethod def f(self, x: float, *args: float) -> float: ... class ModelA(Model): def f(self, x: float) -> float: # 示例操作 return x + 1 class ModelB(Model): def __init__(self, baseline: Model): self.baseline = baseline def f(self, x: float, covar: float, *args: float) -> float: # 示例操作 return x * covar + self.baseline.f(x, *args) def one_future_function(model: Model, x, *args): return model.f(x, *args) modela: Model = ModelA() modelb: Model = ModelB(modela) print(one_future_function(modela, 1)) print(one_future_function(modelb, 1, 2))
当前可行但不满意的优化方案
from abc import ABC, abstractmethod class Model(ABC): @abstractmethod def f(self, x: float, *args: float) -> float: ... class ModelA(Model): def f(self, x: float, *args : float) -> float: return x + 1 class ModelB(Model): def __init__(self, baseline: Model): self.baseline = baseline def f(self, x: float, *args: float) -> float: covar = args[0] return x * covar + self.baseline.f(x, *args[1:])
不满意的原因:ModelB中的covar参数被隐藏在签名之外,而ModelA保留了未使用的*args,会让使用者感到困惑。
核心问题与需求
我的核心问题是:有时f方法需要像代码片段1中的covar这样的额外参数。保持方法签名的显式性很重要,后续用户会期望f方法有时包含covar(或其他参数),有时仅包含x。我希望代码尽可能贴近数学公式,最初使用*args是为了在Model对象组合时利用解包操作,将额外参数传递并被各个基础组件消费。
我寻求一种既符合类型规则,又能在存在额外参数时保持显式方法签名的方案,同时满足以下条件:
- 尽可能不创建额外的Model基类变体(因额外参数种类繁多,会导致基类数量激增);
- 确保派生Model类必须在编译时实现方法(因此使用抽象基类ABC,且继承很重要,需访问Model类中的关键机制);
x参数始终存在于所有具体方法的签名中;- 不依赖特定库;
- 不依赖
**kwargs方案,因为无法命名存在的额外参数(如嵌套ModelB时,重复的covar参数会导致错误)。
若我对OOP原则存在误解,请指出并提供相关学习资源。
尝试过的拆分方案(Function+Model)
我还尝试过将Model的操作拆分到Function类中的方案,但该方案不够直观,且covar未出现在方法签名中,还依赖Python魔术方法,维护难度高:
from abc import ABC, abstractmethod class Function(ABC): @abstractmethod def f(self, x: float) -> float: ... def add_args(*names : str): '''允许添加被视为属性的额外参数''' class FunctionA(Function): def f(self, x: float) -> float: # 示例操作 return x + 1 class FunctionB(Function): def __init__(self, baseline: Function): self.baseline = baseline self.add_args("covar") def f(self, x: float) -> float: # covar通过add_args方法成为属性(实现细节未展示) # 值在Model中设置 return x * self.covar + self.baseline.f(x) class Model: def __init__(self, function : Function): self.function = function def f(self, x : float, *args : float): # 未展示的setter,若function需要参数但未传入会报错 self.function.args = args return self.function.f(x) modela = Model(FunctionA()) modelb = Model(FunctionB(FunctionA())) modela.f(1) # 正常 modelb.f(1, 2) # 正常 modelb.f(1) # 错误:covar值必填
该方案的问题:新增模型时需创建Function对象并传入Model,不够直观,且维护成本高。
内容的提问来源于stack exchange,提问作者kouign amann
相关产品推荐
相关产品推荐

