pythontype-variables

"Type variable has no meaning in this context"


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.


Solution

  • 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]
        ...
    

    Additionally

    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.