Considering the following structure of classes:
from abc import ABC, abstractmethod
class ModelSettings(ABC):
...
class BlueModelSettings(ModelSettings):
...
class RedModelSettings(ModelSettings):
...
class Model(ABC):
@abstractmethod
def compute(self, settings: ModelSettings):
...
class BlueModel(Model):
def compute(self, settings: BlueModelSettings):
...
class RedModel(Model):
def compute(self, settings: RedModelSettings):
...
MyPy is complaining that the implementations of compute
in BlueModel
and RedModel
violate the Liskov substitution principle. I understand this and also understand why it makes sense.
My question is, instead of the above structure, what would be another approach that would satisfy the following requirements:
- base model has a contract that
compute
should receive settings, not apples or oranges - but each specific model should accept only settings relevant to its own model kind
In a way, I essentially want that a subclass' method argument is stricter than what its base class stipulates, which goes directly against the Liskov principle. Which is what's telling me there might be a more suitable approach.
Note:
Model
is library code so it can't know of e.g.BlueModelSettings
which is client code
CodePudding user response:
You need your Model
to be generic in the type of settings
.
T = TypeVar('T', bound=ModelSettings)
class Model(ABC, Generic[T]):
@abstractmethod
def compute(self, settings: T):
...
class BlueModel(Model[BlueModelSettings]):
def compute(self, settings: BlueModelSettings):
...
class RedModel(Model[RedModelSettings]):
def compute(self, settings: RedModelSettings):
...