简体   繁体   中英

How to deal with "Incompatible return value type (got overloaded function)" issue

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?

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM