通过 ParamSpec 输入一个接受参数的装饰器(PEP-612,Python 3.10)
Typing a Decorator that Accepts Arguments via ParamSpec (PEP-612, Python 3.10)
我正在阅读 PEP-612,它使输入装饰器变得相当容易。此外,PEP 中提供的示例使其看起来非常简单。本例直接从 PEP 复制而来:
from typing import ParamSpec, TypeVar
from collections.abc import Callable, Awaitable
P = ParamSpec("P")
R = TypeVar("R")
def add_logging(f: Callable[P, R]) -> Callable[P, Awaitable[R]]:
async def inner(*args: P.args, **kwargs: P.kwargs) -> R:
await log_to_database()
return f(*args, **kwargs)
return inner
@add_logging
def takes_int_str(x: int, y: str) -> int:
return x + 7
await takes_int_str(1, "A") # Accepted
await takes_int_str("B", 2) # Correctly rejected by the type checker
但是,我发现正确键入注释可参数化装饰器并非易事。检查以下 MRE:
import functools
import inspect
from collections.abc import Callable, Coroutine
from typing import ParamSpec, TypeVar
P = ParamSpec("P")
R = TypeVar("R")
def tag(*names: str) -> ??:
"""This decorator just tags an async function with the provided name(s)."""
for name in names:
if not isinstance(name, str):
raise TypeError("tag name must be a string")
def outer(func: Callable[P, R]) -> Callable[P, Coroutine[R]]:
func.tags = names
if not inspect.iscoroutinefunction(func):
raise TypeError("tagged function must be an async function")
@functools.wraps(func)
async def inner(*args: P.args, **kwargs: P.kwargs) -> R:
result = await func(*args, **kwargs)
return result
return inner
return outer
我正在努力弄清楚 tag
函数的 return 类型。另外,我对 outer
和 inner
嵌套函数的输入正确性不是 100% 有信心。我该如何正确输入?
P.S。我知道,到今天为止,mypy 0.902 还没有完全支持这个功能。
首先要注意的是,您的参数化装饰器示例不仅仅是 PEP 的装饰器示例加上参数化。相反,您的第二个示例的装饰器(在参数化之后)采用异步函数,而 PEP 的示例采用同步函数。
因为你是直接 await
ing func
的结果,不像 PEP 的例子await
ed 一个单独的记录器然后正常调用 f
,你的outer
需要取 Callable[[P], Awaitable[R]]
而不是 Callable[[P], R]
.
第二个需要注意的是,关于tag
s return类型,你可以通过添加reveal_type(outer)
来计算它,这将是return类型tag
。我没有 运行 这个(因为 mypy
还没有真正支持你的例子),但它应该说 Callable[[Callable[P, Awaitable[R]]], Callable[P, Awaitable[R]]]
。换句话说,tag
return 是一个本身带有异步函数的装饰器,return 是一个异步函数。
第三点要注意的是,您可能希望在所有示例中使用 Awaitable[T]
(这就是为什么我自己在上面一直使用它的原因)而不是 Coroutine[T]
。这是因为 a) Coroutine
采用三个类型参数而不是一个(所以你必须使用 Coroutine[Any, Any, T]
而不是 Coroutine[T]
,其中前两个类型参数用于 send) and b) Coroutine
is a subtype 的 Awaitable
增加了对 send
ing 的支持,但您无论如何都不会使用它。
我正在阅读 PEP-612,它使输入装饰器变得相当容易。此外,PEP 中提供的示例使其看起来非常简单。本例直接从 PEP 复制而来:
from typing import ParamSpec, TypeVar
from collections.abc import Callable, Awaitable
P = ParamSpec("P")
R = TypeVar("R")
def add_logging(f: Callable[P, R]) -> Callable[P, Awaitable[R]]:
async def inner(*args: P.args, **kwargs: P.kwargs) -> R:
await log_to_database()
return f(*args, **kwargs)
return inner
@add_logging
def takes_int_str(x: int, y: str) -> int:
return x + 7
await takes_int_str(1, "A") # Accepted
await takes_int_str("B", 2) # Correctly rejected by the type checker
但是,我发现正确键入注释可参数化装饰器并非易事。检查以下 MRE:
import functools
import inspect
from collections.abc import Callable, Coroutine
from typing import ParamSpec, TypeVar
P = ParamSpec("P")
R = TypeVar("R")
def tag(*names: str) -> ??:
"""This decorator just tags an async function with the provided name(s)."""
for name in names:
if not isinstance(name, str):
raise TypeError("tag name must be a string")
def outer(func: Callable[P, R]) -> Callable[P, Coroutine[R]]:
func.tags = names
if not inspect.iscoroutinefunction(func):
raise TypeError("tagged function must be an async function")
@functools.wraps(func)
async def inner(*args: P.args, **kwargs: P.kwargs) -> R:
result = await func(*args, **kwargs)
return result
return inner
return outer
我正在努力弄清楚 tag
函数的 return 类型。另外,我对 outer
和 inner
嵌套函数的输入正确性不是 100% 有信心。我该如何正确输入?
P.S。我知道,到今天为止,mypy 0.902 还没有完全支持这个功能。
首先要注意的是,您的参数化装饰器示例不仅仅是 PEP 的装饰器示例加上参数化。相反,您的第二个示例的装饰器(在参数化之后)采用异步函数,而 PEP 的示例采用同步函数。
因为你是直接 await
ing func
的结果,不像 PEP 的例子await
ed 一个单独的记录器然后正常调用 f
,你的outer
需要取 Callable[[P], Awaitable[R]]
而不是 Callable[[P], R]
.
第二个需要注意的是,关于tag
s return类型,你可以通过添加reveal_type(outer)
来计算它,这将是return类型tag
。我没有 运行 这个(因为 mypy
还没有真正支持你的例子),但它应该说 Callable[[Callable[P, Awaitable[R]]], Callable[P, Awaitable[R]]]
。换句话说,tag
return 是一个本身带有异步函数的装饰器,return 是一个异步函数。
第三点要注意的是,您可能希望在所有示例中使用 Awaitable[T]
(这就是为什么我自己在上面一直使用它的原因)而不是 Coroutine[T]
。这是因为 a) Coroutine
采用三个类型参数而不是一个(所以你必须使用 Coroutine[Any, Any, T]
而不是 Coroutine[T]
,其中前两个类型参数用于 send) and b) Coroutine
is a subtype 的 Awaitable
增加了对 send
ing 的支持,但您无论如何都不会使用它。