I am trying to wrap my head around the @overload
operator, generics and the Liskov Substitution Principle. I usually make my abstract classes generic on some typevar T
which is followed by concrete implementations of child classes.
For example please consider the following code:
from typing import TypeVar
E = TypeVar("E") # entity
class AbstractRepository(ABC, Generic[E]):
@abstractmethod
async def update(self, entity: E) -> bool:
raise NotImplementedError
class IndexRepository(AbstractRepository[str]):
@overload
async def update(self, index: str) -> bool:
...
@overload
async def update(self, index: list[str]) -> bool:
...
async def update(self, index: Union[str, list[str]]) -> bool:
# implementation here
return stuff
I expect mypy to complain about the Liskov Substitution Principle, but it does not, even though the implementation of the update
method the parameter index
has a type Union[str, list[str]]
and the child class IndexRepository(AbstractRepository[str])
's signature defines T=str
.
Is this because of the @overload
operator, and why is LSP not violated here?
str
of AbstractRepository.update
is a subtype of the argument type Union[str, list[str]]
of IndexRepository.update
.Therefore, IndexRepository.update
is consistent with IndexRepository
being a subtype of AbstractRepository
and does not violate the LSP.
See also Why is `Callable` generic type contravariant in the arguments?