[Python] Is it possible to type a subclass that only implements a single overload variant?

Discussão em 'Python' iniciado por Stack, Julho 17, 2025.

  1. Stack

    Stack Membro Participativo

    I am trying to add typing to a legacy project that involves a Superclass and Subclasses that only implement specific variants; I am not sure if this is possible.

    Classes expose the same general interface - the API method names are the same, but the input/output might be string based for one and int for another. The various classes all inherit from the base API class that has some core shared functions.

    class API:
    def get_by_ids(
    self,
    ids: Union[List[int], List[str]],
    ) -> Union[Dict[int, Any], Dict[str, Any]]:
    pass


    In the example below, Foo and Bar both implement get_by_ids - however Foo ids are int and Bar ids are str. Both return dict where the submitted ids are keys.

    class Foo(API):

    def get_by_ids(
    self,
    ids: List[int],
    ) -> Dict[int, Any]:
    pass


    class Bar(API):

    def get_by_ids(
    self,
    ids: List[str],
    ) -> Dict[str, Any]:
    pass


    The problem I have run into:

    Using the code above, I run into the expected Liskov substitution principle errors.

    error: Argument 1 of "get_ids" is incompatible with supertype "API"; supertype defines the argument type as "list[int] | list[str]" [override]
    note: This violates the Liskov substitution principle
    note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides


    I extended the code with the following approach to use @overload and define the variants. With that approach, mypy wants all 4 overloads defined in the subclass:

    error: Signature of "get_ids" incompatible with supertype "API" [override]
    note: Superclass:
    note: @overload
    note: def get_ids(self, ids: list[int]) -> dict[int, Any]
    note: @overload
    note: def get_ids(self, ids: list[str]) -> dict[str, Any]
    note: @overload
    note: def get_ids(self, ids: list[tuple[int, int]]) -> dict[tuple[int, int], Any]
    note: @overload
    note: def get_ids(self, ids: list[tuple[str, int]]) -> dict[tuple[str, int], Any]
    note: Subclass:
    note: def get_ids(self, ids: list[int]) -> dict[int, Any]


    Can anyone offer some pointers on how get basic typing into this scenario or if this is even possible? This is a large legacy project - so redesigning is not within the scope of things I can do right now. I wish it were.

    I've tried multiple approaches and permutations of changes, but always end up in a Liskov or missing overloads situation.

    Extended Example

    class API:

    @overload
    def get_by_ids(
    self,
    ids: List[int],
    ) -> Dict[int, Any]: ...

    @overload
    def get_by_ids(
    self,
    ids: List[str],
    ) -> Dict[str, Any]: ...

    def get_by_ids(
    self,
    ids: Union[List[int], List[str]],
    ) -> Union[Dict[int, Any], Dict[str, Any]]:
    pass


    class Foo(API):

    def get_by_ids(
    self,
    ids: List[int],
    ) -> Dict[int, Any]:
    pass


    class Bar(API):

    def get_by_ids(
    self,
    ids: List[str],
    ) -> Dict[str, Any]:
    pass

    Continue reading...

Compartilhe esta Página