I have some machine learning model classes subclassed from XGBoost, Sklearn and TF and I have a factory method that takes a model name and returns a specific implementation and to have a correct return type from the factory I defined a TypeVar. Here MinXGBModel, MinRandomForestModel, MinDenseModel and MinMLPModel are my task-specific subclasses :
from typing import TypeVar
PredictiveModel = TypeVar('PredictiveModel',
MinXGBModel,
MinRandomForestModel,
MinDenseModel,
MinMLPModel)
def predictive_model_factory(model_type: str) -> PredictiveModel:
if model_type == "XGB":
return MinXGBModel
elif model_type == "TF":
return MinDenseModel
elif model_type == "RF":
return MinRandomForestModel
elif model_type == "MLP":
return MinMLPModel
else:
raise NotImplementedError(f'Unknown model type {model_type}. '
'Allowed model types are ("XGB", "RF", "TF", "MLP")')
This works fine but when I actually go to get the model class from a factory:
model_cls: PredictiveModel = predictive_model_factory(model_type=model_type)
the linter highlights reference to PredictiveModel
and says that "Type variable PredictiveModel has no meaning in this context".
I don't use TypeVars very often so probably I'm doing something incorrectly but not sure what exactly. I had a look but couldn't find explanation for this message.
A TypeVar
is used for genetic types, eg. if a function can take an argument of any type and return the same type, you would annotate it as:
T = TypeVar("T")
def same(x: T) -> T:
return x
It means that the return type is the same as the type of argument x
. Note that although the function can return any type, this is not the same as annotating it with Any
.
On the other hand, doing this would have no meaning (there is no way to tell what the type T
is).
T = TypeVar("T")
def same() -> T:
return 7
The question contains similar code, where the type of the returned value cannot be determined.
If the idea is that it can return any of a several types, it should be annotated as a union of types:
def predictive_model_factory(model_type: str
) -> Union[MinXGBModel, MinRandomForestModel,
MinDenseModel, MinMLPModel]
...
BTW, there's is an additional problem in the question. That function's return type is actually type
, because it returns a class and not an instance of a class. So it actually would be correctly annotated as Union[Type[MinXGBModel], Type[MinRandomForestModel], Type[MinDenseModel], Type[MinMLPModel]]
. Now, that is a long type annotation!
This raises other questions:
Why don't those classes all inherit from a base class (let's call it Model
), so that the function can simply return Type[Model]
?
Also, the function could be the factory, instead of just returning a factory. It could instantiate the model and then it's return type would be just Model
.