可调用和可调用生成器与 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 项目的相关问题:
- is inferred when mimicking GADT
- Type variables set to
<nothing>
do not unify with more specific variables when used in a more specific context
更新:更多调试
基本上,如果我们写 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
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 项目的相关问题:
- is inferred when mimicking GADT
- Type variables set to
<nothing>
do not unify with more specific variables when used in a more specific context
更新:更多调试
基本上,如果我们写 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