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)