Python 异步装饰器保留类型 [英] Python Async decorator preserve typing

查看:132
本文介绍了Python 异步装饰器保留类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于以下文件:

from abc import ABC, abstractmethod
from typing import Any, Awaitable, Callable, TypeVar, cast

T = TypeVar('T')


def dec(args: Any) -> Callable[..., Awaitable[T]]:
    def dec2(f: Callable[..., Awaitable[T]]) -> Awaitable[T]:
        async def wrapper(*args:Any, **kwargs:Any) -> T:
            print(args)

            return cast(T, await f(*args, **kwargs))
        return cast(Awaitable[T], wrapper)
    return dec2


class A(ABC):
    @abstractmethod
    async def fn(self) -> 'bool':
        pass

class B(A):
    @dec('hi')
    async def fn(self) -> 'bool':
        return True


class C(A):
    @dec('hi')
    async def fn(self) -> 'bool':
        return False

我收到以下 mypy 错误:

I am receiving the following mypy errors:

$ mypy typetest.py
typetest.py:24: error: Signature of "fn" incompatible with supertype "A"
typetest.py:30: error: Signature of "fn" incompatible with supertype "A"
Found 2 errors in 1 file (checked 1 source file)

输入需要如何工作才能保留类签名并且不接收 mypy 错误.

How does the typing need to work in order to preserve the class signature and not receive the mypy error.

这是在 python3.7 上使用 mypy 0.790

This is on python3.7 with mypy 0.790

推荐答案

TLDR:一个函数声明 async def name(parameters) ->R: 创建一个 (parameters) -> 类型的对象Awaitable[R],而不是 Awaitable[R].这意味着强制转换 cast(Awaitable[T], wrapper) 是错误的,应该被省略,并且各种 Callable 返回类型也必须调整.

TLDR: A function declaration async def name(parameters) -> R: creates an object of type (parameters) -> Awaitable[R], not Awaitable[R]. This means the cast cast(Awaitable[T], wrapper) is wrong and should be omitted, and the various Callable return types must be adjusted as well.

显示 async def 函数(大致是 dec2)执行的简单装饰器看起来像这样:

A naive decorator to show the execution of an async def function (roughly dec2) would look like this:

def show_call(f: Callable[..., Awaitable[T]]) -> Callable[..., Awaitable[T]]:
    async def wrapper(*args: Any, **kwargs: Any) -> T:
        print(f"Executing {f.__name__}")
        return await f(*args, **kwargs)
    return wrapper

接收一个可调用的匹配async def返回一个可调用的匹配async def.换句话说,它保留了一个 async 函数"的一般类型.
请注意,不需要 cast.

This receives an callable matching async def and returns a callable matching async def. In other words, it preserves the general type of "an async function".
Note that there is no cast needed.

由于 Callable 参数被指定为 ...,参数信息丢失.这可以通过 PEP 612 (Python 3.10/typing_extensions) 参数变量,类似于类型变量.

Since the Callable argument was specified as ..., the parameter information is lost. This can be fixed with PEP 612 (Python 3.10 / typing_extensions) parameter variables, which are similar to type variables.

from typing import Any, Awaitable, Callable, TypeVar, ParamSpec

T = TypeVar('T')    # the callable/awaitable return type
P = ParamSpec('P')  # the callable parameters


def dec(message: Any) -> Callable[[Callable[P, Awaitable[T]]], Callable[P, Awaitable[T]]]:
    def dec2(f: Callable[P, Awaitable[T]]) -> Callable[P, Awaitable[T]]:
        async def wrapper(*args: Any, **kwargs: Any) -> T:
            print(message)
            return await f(*args, **kwargs)
        return wrapper
    return dec2

这篇关于Python 异步装饰器保留类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆