可调用和可调用生成器与 typevar 联合的 mypy 错误

mypy error with union of callable and callable generator and typevar

def decorator(
        wrapped: Union[
            Callable[[], T],
            Callable[[], Generator[T, None, None]]
        ]
) -> Callable[[], T]:
    def wrapper():
        value = wrapped()
        if inspect.isgenerator(value):
            return next(value)
        else:
            return value
    return wrapper


@decorator
def foo() -> Generator[str, None, None]:
    yield "bar"

以上代码在mypy中产生如下错误

error: Argument 1 to "decorator" has incompatible type "Callable[[], Generator[str, None, None]]"; expected "Callable[[], Generator[<nothing>, None, None]]"

这是 mypy 的限制还是我做错了什么?

这确实是一个错误。调试后,我发现如果您显式设置 T generic:

U = Union[int, float, str] # your target types here
T = TypeVar('T', U, None)

T = TypeVar('T', None, Any)

它按预期工作。如果我设置 Any,它必须是第二个参数,但是对于联合或标量类型,它正在针对第一个参数进行测试。

这里有几个关于他们 GitHub 项目的相关问题:

更新:更多调试

基本上,如果我们写 TypeVar 而不指定 Any

T = TypeVar('T', Type1, Type2, Type3, ...)

预期参数将仅用第一种类型替换 T Type1

error: Argument 1 to "decorator" has incompatible type "Callable[[], Generator[str, None, None]]"; expected "Union[Callable[[], Type1], Callable[[], Generator[Type1, None, None]]]"

但是如果你这样做:

T = TypeVar('T', Type1, Type2, Type3, Any)

它将忽略所有内容并直接转到Any,没有错误。放置任意数量的 Any

T = TypeVar('T', Any, Any, ..., Any, Type1, Type2, Type3)

将导致忽略所有先前的 Any 类型,并将再次检查 Type1 ,就像根本没有它们一样。但是,如果您在 TypeVar 定义中没有任何内容:

T = TypeVar('T')

您收到 <nothing> 作为生成器的预期输出。

Is there a way to define a typevar that essentially covers everything except Generator? I struggle to find a definition that allows instances of any class but not generators.

基本上没有。来自 PEP 484:

No syntax for listing explicitly raised exceptions is proposed. Currently the only known use case for this feature is documentational, in which case the recommendation is to put this information in a docstring.

Stack Overflow 上也有很多关于此功能的问题:

  • Type hint that excludes a certain type

即使没有这个错误,您仍然会收到与定义相同的结果:

T = TypeVar('T', None, Any)

,因为你的 TypeVar 定义是空的,逻辑上应该等同于 (from docs):

T = TypeVar('T')  # Can be anything