python-3.x - 在 MyPy 中使用 TypeVar 输入带参数的装饰器会产生预期的无人居住类型

标签 python-3.x decorator 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)
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

其用法如下:
@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 的返回类型中使用泛型之间的错误迭代。和一个参数和返回类型 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)

但是,这仍然导致上述错误。这将是我可以接受的事情 #type: ignore正在离开,但不幸的是由于这个问题,任何用这个装饰器装饰的函数都有类型 <uninhabited> ,所以你开始到处失去类型安全。

综上所述(tl;dr):

你如何用参数输入装饰器(不修改函数的签名)?以上是bug吗?它可以解决吗?

MyPy 版本:0.501(发布时的最新版本)

最佳答案

哎呀!看来我搜索的不够仔细。已经有一个问题和解决方法:https://github.com/python/mypy/issues/1551#issuecomment-253978622

关于python-3.x - 在 MyPy 中使用 TypeVar 输入带参数的装饰器会产生预期的无人居住类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43013083/

相关文章:

python - 导入错误 : No module named 'util'

python - 检查python/pandas中列之间的关系类型? (一对一,一对多或多对多)

javascript - 在将剩余 (...theArgs) 参数传递给函数之前对其进行解压

Python 装饰器和嵌套函数返回语句。带括号的正确用法?

python - 如何显示装饰器的原始参数

python - 如何对多个字段分组的数量求和?

python循环requests.get()只返回第一个循环

python - python 中的专用@property 装饰器

javascript - f(arguments) 和 f.apply(this,arguments) 有什么区别?

python - 如何通过类装饰器通过 `functools.wraps`访问装饰类的属性