这不是一个大问题,但我只是想知道解决这个问题的方法。由于我刚开始在Python上使用函数注释,所以我不熟悉它。我在下面有一个问题。
当你制作一个装饰器并想在其上添加注释时,你该怎么做?
例如,如下代码。
def decorator(func: Callable[[*args,**kwargs], <what type should be here?>]) -> <??>:
def new_func(*args, **kwargs):
return func(*args, **kwargs)
return new_func
Run Code Online (Sandbox Code Playgroud)
Nik*_*nes 18
请注意,PEP 612(在 Python 3.10 中实现)引入了ParamSpec
,它解决了您的问题,如下所示:
from typing import Callable, TypeVar, ParamSpec
T = TypeVar('T')
P = ParamSpec('P')
def decorator(func: Callable[P, T]) -> Callable[P, T]:
def new_func(*args: P.args, **kwargs: P.kwargs) -> T:
return func(*args, **kwargs)
return new_func
Run Code Online (Sandbox Code Playgroud)
我实际上认为@Nikola Benes
正确的答案不是我,即:
PEP 612
引入,它提供了定义可调用参数之间的依赖关系的ParamSpec
能力。
以下是您以前可以尝试ParamSpec
的一种方法,但这ParamSpec
是可行的方法。
对于使用 Python <3.10 的用户,您应该能够ParamSpec
从typing_extensions
from typing_extensions import ParamSpec
Run Code Online (Sandbox Code Playgroud)
但我还没有尝试过。它还可能取决于您的静态类型检查器(例如mypy
,pyright
等)以及该检查器的版本是否已实现对其的支持。
PyCon 2022 Typing Summit视频录制的第一部分正在ParamSpec
实际演示。
用于Any
返回类型并返回另一个Callable
返回类型Any
。从PEP 484和python 标准库开始,第一个参数Callable
必须是可调用参数的类型,而不是参数本身。因此,您对*args
and **kwargs
in的使用Callable
是不可接受的。相反,您必须使用省略号...
(它允许任意数量的位置和关键字参数类型)。
使用泛型类型 ( ) 可以更清晰地表达装饰器函数typing.TypeVar
。通俗地说,泛型就是允许类型作为参数的东西。
摘自mypy 文档(仅供参考:mypy
是一个静态类型检查器包python
):
装饰器函数可以使用泛型类型来表达。泛型可以限制为使用带有关键字参数的特定类型的子类型的值
bound=...
。上限可用于保留装饰器装饰的包装器函数的签名。
因此,你的例子变成这样:
from typing import Any, Callable, TypeVar, cast
F = TypeVar('F', bound=Callable[..., Any])
def decorator(func: F) -> F:
def new_func(*args, **kwargs):
return func(*args, **kwargs)
return cast(F, new_func)
Run Code Online (Sandbox Code Playgroud)
使用绑定 on
F
以便在非函数上调用装饰器将被拒绝。此外,包装函数 (new_func
) 没有进行类型检查,因为(当前)不支持使用可变数量的特定类型参数来指定回调签名,因此我们必须在最后强制转换类型。