How does mypy apply the Liskov substitution principle to *args, **kwargs
parameters?
I thought the following code should fail a mypy check since some calls to f
allowed by the Base
class are not allowed by C
, but it actually passed. Are there any reasons for this?
from abc import ABC, abstractmethod
from typing import Any
class Base(ABC):
@abstractmethod
def f(self, *args: Any, **kwargs: Any) -> int:
pass
class C(Base):
def f(self, batch: int, train: bool) -> int:
return 1
I also tried to remove either *args
or **kwargs
, both failed.
Unlike Daniil said in currently accepted answer, the reason is exactly (*args: Any, **kwargs: Any)
signature part.
Please check the corresponding discussion on mypy
issue tracker:
I actually like this idea, I have seen this confusion several times, and although it is a bit unsafe, most of the time when people write (*args, **kwargs) it means "don't care", rather than "should work for all calls".
[GVR] Agreed, this is a case where practicality beats purity.
So, mypy
gives a special treatment to functions of form
# _T is arbitrary type
class _:
def _(self, *args, **kwargs) -> _T: ...
and considers them fully equivalent to Callable[..., _T]
.
Yes, this actually violates LSP, of course, but this was designed specially to allow declaring functions with signature "just ignore my parameters".
To declare the broadest possible function that really accepts arbitrary positional and keyword arguments, you should use object
in signature instead.