装饰器的Python 3类型提示 [英] Python 3 type hinting for decorator
问题描述
请考虑以下代码:
from typing import Callable, Any
TFunc = Callable[..., Any]
def get_authenticated_user(): return "John"
def require_auth() -> Callable[TFunc, TFunc]:
def decorator(func: TFunc) -> TFunc:
def wrapper(*args, **kwargs) -> Any:
user = get_authenticated_user()
if user is None:
raise Exception("Don't!")
return func(*args, **kwargs)
return wrapper
return decorator
@require_auth()
def foo(a: int) -> bool:
return bool(a % 2)
foo(2) # Type check OK
foo("no!") # Type check failing as intended
这段代码按预期工作。现在想象一下,我想扩展它,而不是仅仅执行 func(* args,** kwargs)
,我想将用户名注入参数中。因此,我修改了函数签名。
This piece of code is working as intended. Now imagine I want to extend this, and instead of just executing func(*args, **kwargs)
I want to inject the username in the arguments. Therefore, I modify the function signature.
from typing import Callable, Any
TFunc = Callable[..., Any]
def get_authenticated_user(): return "John"
def inject_user() -> Callable[TFunc, TFunc]:
def decorator(func: TFunc) -> TFunc:
def wrapper(*args, **kwargs) -> Any:
user = get_authenticated_user()
if user is None:
raise Exception("Don't!")
return func(*args, user, **kwargs) # <- call signature modified
return wrapper
return decorator
@inject_user()
def foo(a: int, username: str) -> bool:
print(username)
return bool(a % 2)
foo(2) # Type check OK
foo("no!") # Type check OK <---- UNEXPECTED
我不知道正确的方法键入方式。我知道在此示例中,修饰函数和返回函数在技术上应具有相同的签名(但即使未被检测到)。
I can't figure out a correct way to type this. I know that on this example, decorated function and returned function should technically have the same signature (but even that is not detected).
推荐答案
您不能使用 Callable
谈论其他参数。它们不是通用的。唯一的选择是说您的装饰器采用 Callable
,并返回不同的 Callable
。
You can't use Callable
to say anything about additional arguments; they are not generic. Your only option is to say that your decorator takes a Callable
and that a different Callable
is returned.
在您的情况下,您可以可以使用类型变量确定返回类型:
In your case you can nail down the return type with a typevar:
RT = TypeVar('RT') # return type
def inject_user() -> Callable[[Callable[..., RT]], Callable[..., RT]]:
def decorator(func: Callable[..., RT]) -> Callable[..., RT]:
def wrapper(*args, **kwargs) -> RT:
# ...
即使是装饰后的 foo()
函数的键入签名为 def(* Any,** Any)->当您使用
。 reveal_type()
时,builtins.bool *
Even then the resulting decorated foo()
function has a typing signature of def (*Any, **Any) -> builtins.bool*
when you use reveal_type()
.
当前有各种建议正在讨论使 Callable
更加灵活,但这些措施尚未实现。参见
Various proposals are currently being discussed to make Callable
more flexible but those have not yet come to fruition. See
- Allow variadic generics
- Proposal: Generalize
Callable
to be able to specify argument names and kinds - TypeVar to represent a Callable's arguments
- Support function decorators excellently
例如。该列表中的最后一个是一张总括票,其中包括您的特定用例,修饰器,该修饰器更改了可调用的签名:
for some examples. The last one in that list is an umbrella ticket that includes your specific usecase, the decorator that alters the callable signature:
Mess with return类型或带有参数
对于任意函数,您根本无法做到这一点-甚至没有语法。这是我为它编写的一些语法。
Mess with the return type or with arguments
For an arbitrary function you can't do this at all yet -- there isn't even a syntax. Here's me making up some syntax for it.
这篇关于装饰器的Python 3类型提示的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!