如何正确输入包装函数?

How do I type correctly a wrapper function?

假设以下声明:

from typing import Callable, TypeVar
T = TypeVar('T')

def wrapper(fn: Callable[..., T]) -> Callable[..., T]:
    ...

def identity(a: T) -> T:
    ...

@wrapper
def w_wrapped(a: T) -> T:
    ...

@identity
def i_wrapped(a: T) -> T:
    ...

两个注解函数可以这样使用:

def apply(fn: Callable[[str], int]) -> int:
    # types fine:
    val1 = fn(i_wrapped(''))
    # mypy complains: error: Argument 1 has incompatible type "T"; expected "str"
    val2 = fn(w_wrapped(''))
    return val1 + val2

Callable 类型有什么问题?我可以在 wrapper 声明中使用 Callable[..., Any] 而不是 Callable[..., T]。但我觉得这部分违背了目的,我想声明,当你将包装器与 str 一起使用时,结果将是 str,而不是任何东西。可能还有其他解决方法,但这是 mypy 的限制还是我的误解?

当你写 Callable[..., T] 时,我相信编辑器无法将“...”与“T”关联起来。 “apply”fn 期待一个 str,w_wrapped 返回一个 str,但这个过程发生在“wrapper”内部逻辑中,因为“...”与 T 无关,编辑器正在处理两个不同的T,例如 T_0 和 T_1。也许这就是导致类型不匹配的原因。

我认为这里可能有两件事:

首先,一个mypy的bug,见this and this

其次,Callable[..., T]太松了。具体来说,它的参数与其 return 值之间没有任何联系。结果,对于 @wrapperw_wrapped 变成了 Callable[..., T],对其参数没有约束,而 w_wrapped('') 的输出是一个未绑定的 T,这无法传递给需要 str.

fn

根据您的用例,您有多种选择,包括

  • def wrapper(fn: Callable[[U], T]) -> Callable[[U], T]: for U = TypeVar('U'),尽管我相信 mypy 错误会阻止此工作。 U 也可以是 T
  • def wrapper(fn: C) -> C: C = TypeVar('C', bound=Callable)。这没有 Any 的问题,因为你在 Callable 边界 所以你保留了类型签名。但是,它会限制您对 wrapper 的实施,而不是 type: ignore