I want to create an abstract base class with an abstract method that returns an instance of any subclass that implements the interface. Is there a more pythonic way to achieve this for mypy other than what I have created below?
In my code example, I make the Animal
class generic. Subclasses can inherit from Animal and specify the generic parameter as itself but it seems clunky and wrong to me. I think I may be missing something obvious.
Notice that in the code below, when I subclass Animal
the class definition uses something like Dog(Animal["Dog"])
. It doesn’t look right to me, but it works for type checking. Is there a way to indicate for an abstract method that it must return the same type as self
?
import abc
from typing import Generic, TypeVar
from __future__ import annotations
T = TypeVar('T')
class Animal(abc.ABC, Generic[T]):
@abc.abstractmethod
def procreate(self: T, other: T) -> T:
pass
class Dog(Animal["Dog"]):
def procreate(self, other: "Dog"):
return Dog()
class Cat(Animal["Cat"]):
def procreate(self, other: "Cat"):
return Cat()
dog = Dog()
dog.procreate(Cat())
Advertisement
Answer
AFAIK you don’t need your Animal
class to be Generic
unless it is some kind of container, e.g. Sequence
, we can just use TypeVar
for a particular method
So this should work as intended
import abc
from typing import TypeVar
from __future__ import annotations
T = TypeVar('T')
class Animal(abc.ABC):
@abc.abstractmethod
def procreate(self: T, other: T) -> T:
pass
class Dog(Animal):
def procreate(self, other: "Dog") -> "Dog":
return Dog()
class Cat(Animal):
def procreate(self, other: "Cat") -> "Cat":
return Cat()
dog = Dog()
dog.procreate(Cat())
mypy
will inform about an error on a last line:
> mypy test.py
test.py:26: error: Argument 1 to "procreate" of "Dog" has incompatible type "Cat"; expected "Dog"
Found 1 error in 1 file (checked 1 source file)