I have a function that accepts a Callable as one of its parameters, and returns whatever that Callable returns. So I want to enforce that the return type of my function do_stuff()
is the same as the return type of its parameter some_callable
.
MyPy has no complaints about this:
CallableResult = TypeVar("CallableResult")
def do_stuff(
some_text: Text,
some_callable: Callable[[Text], CallableResult],
) -> CallableResult:
"""Do stuff"""
return some_callable(some_text)
I'd like to also supply a default argument for the Callable parameter some_callable
, such as 'str':
def do_stuff_with_default(
some_text: Text,
some_callable: Callable[[Text], CallableResult] = str,
) -> CallableResult:
"""Do stuff"""
return some_callable(some_text)
But then MyPy raises an error on the parameter declaration for some_callable
:
error: Incompatible default for argument "some_callable" (default has type "Type[str]", argument has type "Callable[[str], CallableResult]")
That confuses me, because while str
IS a Type
, isn't it also a Callable
? If so, why does MyPy reject str
as the parameter default value?
I can silence the error by adding constraints to my TypeVar()
declaration:
CallableResult = TypeVar("CallableResult", Any, Text)
But I'm not sure is this actually enforces my function's return type in the way that I want.
CodePudding user response:
As Guido Van Rossum pointed out in this mypy issue you can use typing.overload
:
from typing import TYPE_CHECKING, Any, Callable, TypeVar, overload
from typing_extensions import reveal_type
Text = Any # or whatever Text is in your case
CallableResult = TypeVar("CallableResult")
@overload
def do_stuff_with_default(
some_text: Text,
some_callable: Callable[[Text], CallableResult],
) -> CallableResult:
...
@overload
def do_stuff_with_default(
some_text: Text,
) -> str:
...
def do_stuff_with_default(
some_text: Text,
some_callable: Callable[[Text], CallableResult] | Callable[..., str] = str,
) -> CallableResult | str:
"""Do stuff"""
return some_callable(some_text)
x = do_stuff_with_default(1)
y = do_stuff_with_default(1, int)
if TYPE_CHECKING:
reveal_type(x) # revealed type is str
reveal_type(y) # revealed type is int