Reputation: 5016
I'm trying to define a function which returns another function. The function it returns is overloaded.
For example:
from typing import overload, Union, Callable
@overload
def foo(a: str) -> str:
pass
@overload
def foo(a: int) -> int:
pass
def foo(a: Union[str, int]) -> Union[str, int]:
if isinstance(a, int):
return 1
else:
# str
return "one"
def bar() -> Callable[[Union[str, int]], Union[str, int]]:
return foo # Incompatible return value type (got overloaded function, expected "Callable[[Union[str, int]], Union[str, int]]")
However, my typing for the function bar
is coming up as an error using Mypy.
How do I type bar
correctly? What am I doing wrong?
Upvotes: 2
Views: 5749
Reputation: 1
I sorted that out by changing to pyre:
from typing import overload, Union
def foo(a: Union[str, int]) -> Union[str, int]:
if isinstance(a, int):
return 1
else:
return 'a string'
check:
(.penv) nick$: pyre check
ƛ No type errors found
Upvotes: 0
Reputation: 63978
The issue here is partly that the Callable
type is a little too limited to accurately express the type for foo
and also partly that mypy is currently very conservative when analyzing the compatibility of overloads against Callables. (It's hard to do in the general case).
Probably the best approach for now is to just define a more precise return type by using Callback protocol and return that instead:
For example:
from typing import overload, Union, Callable
# Or if you're using Python 3.8+, just 'from typing import Protocol'
from typing_extensions import Protocol
# A callback protocol encoding the exact signature you want to return
class FooLike(Protocol):
@overload
def __call__(self, a: str) -> str: ...
@overload
def __call__(self, a: int) -> int: ...
def __call__(self, a: Union[str, int]) -> Union[str, int]: ...
@overload
def foo(a: str) -> str:
pass
@overload
def foo(a: int) -> int:
pass
def foo(a: Union[str, int]) -> Union[str, int]:
if isinstance(a, int):
return 1
else:
# str
return "one"
def bar() -> FooLike:
return foo # now type-checks
Note: Protocol
was added to the typing
module as of Python 3.8. If you want it in earlier versions of Python, install the typing_extensions module (
pip install typing_extensions`) and import it from there.
Having to copy the signature like this twice is admittedly a bit clunky. People generally seem to agree that this is a problem (there are various issues about this in the typing and mypy issue trackers), but I don't think there's any consensus on how to best solve this yet.
Upvotes: 3