TLDR: Make the baseclass Generic
and parameterise the type of configuration:
C = TypeVar('C', bound=BaseConfig)
class Base(abc.ABC, Generic[C]):
@abc.abstractmethod
def do_something(self, config: C):
pass
The original class hierarchy declares that ClassA
can be used anywhere Base
is valid. When we assume some variable obj: Base
, this leads to a conflict:
- We can assign
obj = ClassA()
since ClassA
"is a" Base
class.
- We can use
obj.do_something(BaseConfig())
since obj
"is a" Base
instance.
However, ClassA.do_something(config: ConfigA)
says we cannot do both at the same time, contradicting the type equivalence.
Instead, we need to distinguish between "Base
that takes a ConfigA
", "Base
that takes a ConfigB
" and so on. This is done by parameterising Base
with a type-variable for the config.
from typing import Generic, TypeVar
C = TypeVar('C', bound=BaseConfig) # C "is some" BaseConfig type
class Base(abc.ABC, Generic[C]): # class takes type variable ...
@abc.abstractmethod
def do_something(self, config: C): # ... and uses it in method signature
pass
This allows us to have both generic and concrete Base
variants - for example, Base[ConfigA]
is a "Base
that takes a ConfigA
". From this, the subclasses can be derived as taking the appropriate configuration:
class ClassA(Base[ConfigA]): # set type variable to ConfigA
def do_something(self, config: ConfigA):
print("option_a: " + config.option_a)
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…