MyPy与 Callable
*args
和 **kwargs
有一些问题,特别是有关装饰器的问题,详见:https://github.com/python/mypy/issues/1927
具体来说,对于没有参数只包装函数(并且不更改其签名)的装饰器,您需要以下内容:
from typing import Any, Callable, cast, TypeVar
FuncT = TypeVar('FuncT', bound=Callable[..., Any])
def print_on_call(func: FuncT) -> FuncT:
def wrapped(*args, **kwargs):
print("Running", func.__name__)
return func(*args, **kwargs)
return cast(FuncT, wrapped)
最后的 cast()
应该是不必要的(MyPy应该能够通过在 wrapped
末尾调用 func
来实现,包裹的确是 FuncT -> FuncT
) . 我可以忍受这个,直到它被修复 .
然而,当您引入带参数的装饰器时,这会破坏性 . 考虑装饰者:
def print_on_call(foo):
def decorator(func):
def wrapped(*args, **kwargs):
print("Running", foo)
return func(*args, **kwargs)
return wrapped
return decorator
用作如下:
@print_on_call('bar')
def stuff(a, b):
return a + b
我们可能会尝试输入它(使用Guido认可的无参数示例作为指导),如下所示:
from typing import Any, Callable, Dict, List, TypeVar
FuncT = TypeVar('FuncT', bound=Callable[..., Any])
def print_on_call(foo: str) -> Callable[[FuncT], FuncT]:
def decorator(func: FuncT) -> FuncT:
def wrapped(*args: List[Any], **kwargs: Dict[str, Any]) -> Any:
print("Running", foo)
return func(*args, **kwargs)
return cast(FuncT, wrapped)
return cast(Callable[[FuncT], FuncT], decorator)
这似乎是类型检查,但是当我们使用它时:
@print_on_call('bar')
def stuff(a: int, b: int) -> int:
return a + b
我们得到一个令人讨厌的错误:
error: Argument 1 has incompatible type Callable[[int, int], int]; expected <uninhabited>
我对这怎么可能感到困惑 . 如PEP 484中所述,似乎 Callable[[int, int], int]
应该是 Callable[..., Any]
的子类型 .
我假设这可能是在 print_on_call
的返回类型和aa参数之间使用泛型并且返回类型为 decorator
之间的错误迭代,所以我将我的示例削减到最低限度(虽然不再是工作装饰器,但它仍然应该是typecheck ):
from typing import Any, Callable, Dict, List, TypeVar
FuncT = TypeVar('FuncT', bound=Callable[..., Any])
def print_on_call(foo: str) -> Callable[[FuncT], FuncT]:
return cast(Callable[[FuncT], FuncT], None)
但是,这仍然会导致上述错误 . 这只是_808682远离我会好的,但遗憾的是由于这个问题,装饰这个装饰器的任何函数都有 <uninhabited>
类型,所以你开始失去类型安全的地方 .
That all said (tl;dr):
如何使用参数键入装饰器(不修改函数的签名)?上面是一个bug吗?可以解决吗?
MyPy版本:0.501(此帖子的最新内容)
1 回答
哎呀!看起来我搜索不够努力 . 已经有一个问题和解决方法:https://github.com/python/mypy/issues/1551#issuecomment-253978622