Skip to content
Advertisement

Abstract base class using type of subclass in abstractmethods

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)
User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement