Python 3.x 使用TypeVar在MyPy中使用参数键入Decorator将生成预期的未占用类型

Python 3.x 使用TypeVar在MyPy中使用参数键入Decorator将生成预期的未占用类型,python-3.x,decorator,python-decorators,mypy,Python 3.x,Decorator,Python Decorators,Mypy,MyPy在可调用*args和**kwargs方面存在一些问题,特别是在装饰器方面,详见: 具体来说,对于没有参数的装饰器,它只包装函数(并且不更改其签名),您需要以下内容: from typing import Any, Callable, cast, TypeVar FuncT = TypeVar('FuncT', bound=Callable[..., Any]) def print_on_call(func: FuncT) -> FuncT: def wrapped(*a

MyPy在
可调用
*args
**kwargs
方面存在一些问题,特别是在装饰器方面,详见:

具体来说,对于没有参数的装饰器,它只包装函数(并且不更改其签名),您需要以下内容:

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
来推导出,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
其用途如下:

@print_on_call('bar')
def stuff(a, b):
    return a + b
我们可以尝试这样键入它(使用Guido认可的无参数示例作为指南):

这看起来像是打字检查,但当我们使用它时:

@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>
然而,这仍然会导致上述错误。这将是我可以接受的,只要
#type:ignore
ing离开,但不幸的是,由于这个问题,任何用这个装饰器装饰的函数都有类型
,所以你开始到处失去类型安全性

所有人都说了(tl;dr):

如何使用参数键入decorators(不修改函数的签名)?以上是一个错误吗?这能解决吗


MyPy版本:0.501(最新发布日期)

Oops!看来我搜索得不够努力。这方面已经存在一个问题和解决方法:

现在,mypy直接支持这一点:

i、 e

FuncT=TypeVar(“FuncT”,bound=Callable[…,Any])
def my_decorator(func:func)->func:
@包装(func)
def wrapped(*args:Any,**kwargs:Any)->Any:
打印(“某物”)
返回函数(*args,**kwargs)
返回强制转换(函数,已包装)
TypeVar(F,…,耶)
error: Argument 1 has incompatible type Callable[[int, int], int]; expected <uninhabited>
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)