Bai*_*ker 7 decorator python-3.x python-decorators mypy
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)
Run Code Online (Sandbox Code Playgroud)
所述cast()
在端部应该是不必要的(MyPy应该能够推导出通过调用func
在结束wrapped
该包裹的确是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
Run Code Online (Sandbox Code Playgroud)
其用法如下:
@print_on_call('bar')
def stuff(a, b):
return a + b
Run Code Online (Sandbox Code Playgroud)
我们可能会尝试键入它(使用 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)
Run Code Online (Sandbox Code Playgroud)
这似乎是类型检查,但是当我们使用它时:
@print_on_call('bar')
def stuff(a: int, b: int) -> int:
return a + b
Run Code Online (Sandbox Code Playgroud)
我们得到一个严重的错误:
error: Argument 1 has incompatible type Callable[[int, int], int]; expected <uninhabited>
Run Code Online (Sandbox Code Playgroud)
我有点困惑这怎么可能。正如PEP 484 中所讨论的,这似乎Callable[[int, int], int]
应该是Callable[..., Any]
.
我认为这可能是在返回类型 ofprint_on_call
和 aa 参数和返回类型 to之间使用泛型之间的错误迭代decorator
,因此我将示例缩减到最低限度(尽管不再是工作装饰器,但仍应进行类型检查):
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)
Run Code Online (Sandbox Code Playgroud)
但是,这仍然导致上述错误。这将是我可以接受的事情#type: ignore
,但不幸的是由于这个问题,用这个装饰器装饰的任何函数都有 type <uninhabited>
,所以你开始到处失去类型安全。
这一切都说(tl; dr):
你如何用参数输入装饰器(不修改函数的签名)?以上是bug吗?它可以解决吗?
MyPy 版本:0.501(发布时的最新版本)
Seb*_*ner 13
如今,mypy 直接支持这一点:
https://mypy.readthedocs.io/en/stable/generics.html#declaring-decorators
IE
FuncT = TypeVar("FuncT", bound=Callable[..., Any])
def my_decorator(func: FuncT) -> FuncT:
@wraps(func)
def wrapped(*args: Any, **kwargs: Any) -> Any:
print("something")
return func(*args, **kwargs)
return cast(FuncT, wrapped)
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
4256 次 |
最近记录: |