Search code examples
pythonpython-typing

Instantiate a type that is a TypeVar


As a C++ programmer the following code seems very natural to me but it doesn't run:

from typing import TypeVar, Generic, List, NewType

TPopMember = TypeVar('TPopMember')
Population = NewType('Population', List[TPopMember])
class EvolutionaryAlgorithm(Generic[TPopMember]):
    def __init__(self, populationSize: int) -> None:
        # The following raises TypeError: 'TypeVar' object is not callable
        self.__population = Population([TPopMember() for _ in range(populationSize)])

Apparently Python isn't able to instantiate classes (the TPopMember) that are actually TypeVars. I simply want to create a list (Population) with a couple of default initialized (how do you say that in Python?) TPopMembers. How should I go about this?

I'm using Python 3.7.2.


Solution

  • You didn't realize that type hint is a hint. In other words, don't think it is a type at all. You can't instantiate them.

    As I understand from your comment, your intention is doing what C++ template allows you to do. So here is my way to achieve that:

    from typing import TypeVar, Generic, List, NewType, Type
    import random
    
    class PopMember:
        def __init__(self):
            self.x = random.randint(0, 100)
        def __repr__(self):
            return "Pop({})".format(self.x)
    
    TPopMember = TypeVar("TPopMember")
    Population = NewType('Population', List[TPopMember])
    
    class EvolutionaryAlgorithm(Generic[TPopMember]):
        def __init__(self, member_class: Type[TPopMember], populationSize: int) -> None:
            self.__population = Population([member_class() for _ in range(populationSize)])
        def __repr__(self):
            return "EA({})".format(self.__population)
    
    x = EvolutionaryAlgorithm(PopMember, 5)
    print(x)
    

    output:

    EA([Pop(49), Pop(94), Pop(24), Pop(73), Pop(66)])
    

    What you have to understand is that, if you derived a class from Generic[T], you need to use T some how when you create your class. In my example I create a dummy object and resolve its class and initiate it. Normally I would not write in this way, I can just throw in a class as parameber pass in a class to the constructor to request to generate items of this particular type because class itself, distinct from an instance of it, is also a Python object. (thanks chepner for the suggestion)