【问题标题】:Python Async decorator preserve typingPython Async 装饰器保留输入
【发布时间】:2021-01-12 18:09:09
【问题描述】:

对于以下文件:

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 错误:

$ 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 错误。

这是在 python3.7 上,mypy 0.790

【问题讨论】:

  • 尝试使用-> bool 而不是-> 'bool'。返回类型不应需要引号,这可能会干扰签名。
  • 报价有效。唯一的区别是在运行时不会进行名称查找。使用 from __future__ import annotations 会导致 all 注释被隐式视为字符串文字(我相信这将是 Python 3.10 中的默认设置)。

标签: python python-3.x mypy


【解决方案1】:

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


显示async def 函数(大致为dec2)执行的天真装饰器如下所示:

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

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

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

【讨论】:

猜你喜欢
  • 2017-07-10
  • 1970-01-01
  • 1970-01-01
  • 2019-03-11
  • 2020-11-02
  • 2012-02-18
  • 1970-01-01
  • 1970-01-01
  • 2022-09-29
相关资源
最近更新 更多