Assume that an abstract base class MembershipClass
has been created. Multiple classes are derived from the abstract base class, e.g., FirstClass
, SecondClass
, etc.
I wish to use type annotations in a function that accepts as an argument any class derived from MembershipClass
. If there are a small number of derived classes (say 2), this should work:
from typing import Union
def MyFunc(membership_obj: Union[FirstClass, SecondClass]) -> None:
...
Is there a way to create a type hint for membership_obj
which essentially says that its type is any class derived from MembershipClass
without having to specify each possible derived class in the type annotation?
I have seen two possible solutions:
from typing import TypeVar
BaseType = TypeVar('BaseType', bound=MembershipClass)
def MyFunc(membership_obj: BaseType) -> None:
...
def MyFunc(membership_obj: MembershipClass) -> None:
...
Is either approach acceptable?
It looks like both solutions will work, although the mypy messages are slightly different. Consider the following example (I've added mypy errors inline):
from abc import ABC
from typing import TypeVar
class Base(ABC):
pass
class Sub(Base):
pass
BaseType = TypeVar("BaseType", bound=Base)
def MyFunc(c: Base) -> None:
pass
def MyFunc2(c: BaseType) -> None:
pass
if __name__ == "__main__":
b = Base()
s = Sub()
MyFunc(b)
MyFunc(s)
MyFunc(3) # main.py:30: error: Argument 1 to "MyFunc" has incompatible type "int"; expected "Base"
MyFunc2(b)
MyFunc2(s)
MyFunc2(3) # main.py:34: error: Value of type variable "BaseType" of "MyFunc2" cannot be "int"
That being said, I think the second method is more readable and intuitive. I think that TypeVar
is more suited for generics (that's not to say you shouldn't use it if you want to).