如何处理“不兼容的返回值类型(重载函数)”问题

时间:2019-06-17 04:47:42

标签: python python-3.x typing mypy

我正在尝试定义一个函数,该函数返回另一个函数。它返回的函数已重载。

例如:

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

但是,使用Mypy输入函数bar时出现错误。

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

2 个答案:

答案 0 :(得分:2)

这里的问题部分是Callable类型太有限而无法准确地表示foo的类型,部分原因是在分析重载与Callables的兼容性时,mypy当前非常保守。 (在一般情况下很难做到这一点。)

目前最好的方法可能是使用Callback protocol定义更精确的返回类型,然后返回该值:

例如:

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

注意:Protocol已从Python 3.8开始添加到typing模块中。如果您希望在Python的早期版本中使用它,请安装typing_extensions module ( pip installTypeing_extensions`),然后从那里导入。

必须像这样两次复制签名,这有点笨拙。人们通常似乎都同意这是一个问题(typingmypy问题跟踪器中存在与此有关的各种问题),但我认为尚未就如何最好地解决此问题达成共识

答案 1 :(得分:0)

我通过更改为柴堆来解决了这个问题:

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