如何处理 "Incompatible return value type (got overloaded function)" 问题

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

我正在尝试定义一个 returns 另一个函数的函数。它returns 的函数被重载了。

例如:

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]]")

但是,我对函数 bar 的键入在使用 Mypy 时出现错误。

如何正确输入 bar?我做错了什么?

这里的问题部分是 Callable 类型有点太有限,无法准确表达 foo 的类型,部分是 mypy 目前在分析重载的兼容性时非常保守可调用对象。 (一般情况下很难做到)。

目前最好的方法可能是使用 Callback protocol 和 return 来定义更精确的 return 类型,而不是:

例如:

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

注意:自 Python 3.8 起,Protocol 已添加到 typing 模块。如果你想在 Python 的早期版本中使用它,请安装 typing_extensions module (pip install typing_extensions`) 并从那里导入它。

不得不像这样复制签名两次确实有点笨拙。人们普遍认为这是一个问题(typing and mypy 问题跟踪器中有各种关于此的问题),但我认为目前还没有就如何最好地解决这个问题达成共识。

我通过更改为 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'

检查:

    (.penv) nick$: pyre check
     ƛ No type errors found