A way to typehint a return type in parent based on return type of a child

Both in my personal projects and at two of my workplaces I have found a specific case that python typehints cannot handle well enough.

from abc import abstractmethod, ABC


class A(ABC):
    def process_child_method(self):
        child_return = self.child_method()
        # Some actions on the return of the method
        ...
        return child_return

    @abstractmethod
    def child_method(self):
        pass


class B(A):
    def child_method(self) -> int:
        return 83


b = B()
b.process_child_method()  # None, according to pyright

The only way to handle it right now is to use Generics but that creates a weird decoupling in terms of typehints:

from typing import Generic, TypeVar
from abc import abstractmethod, ABC


T = TypeVar("T")


class A(ABC, Generic[T]):
    def process_child_method(self) -> T:
        child_return = self.child_method()
        # Some actions on the return of the method
        ...
        return child_return

    @abstractmethod
    def child_method(self) -> T:
        pass


class B(A[int]):
    def child_method(self) -> int:
        return 83


b = B()
b.process_child_method()  # int

which could easily lead to typehints lying in large classes where we are not careful such as:

class C(A[int]):
    # Many lines of code here
    ...
    def child_method(self) -> str:
        return "83"

My examples are a bit esoteric and I do not necessarily consider this to be a good pattern but I believe we should have some way of describing “returns the same object as another method in the class”.

Let’s discuss it. I guess this introduces dependencies into typehints which could become rather buggy and hard to manage but maybe we can figure out some nice way of handling such cases. Something like a DependencyVariable or even just adding support for such cases into TypeVar.

I also understand that this is mostly the business of the tool creators but the original peps for typing also haven’t covered this use case.

You might want to raise this on typing-sig or python/typing.

A

2 Likes

mypy’s fine, generics are fine, the problem was with pyright’s basic mode and pycharm.

1 Like

I think abstract class should define common interface for all the sub-classes. So the type hint of the abstract method should cover all intended implementations.

In this sense, you can use for example, if you plan some implementations to return int and some str:

    @abstractmethod
    def child_method(self) -> str | int:
        pass
1 Like

You are right! And when I write my own projects — I don’t usually need this pattern. But at my current and prior workplaces large subsystems were based on this pattern so refactoring it would be incredibly painful.

I.e. it’s a bad pattern but we should be able to typehint it nonetheless (which we can as I mentioned in my previous comment)