考虑以下代码:
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)
我想在参数中注入用户名。因此,我修改了函数签名。
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
我想不出正确的输入方式。我知道在这个例子中,装饰函数和返回函数在技术上应该具有相同的签名(但即使这样也没有被检测到)。
原文由 FunkySayu 发布,翻译遵循 CC BY-SA 4.0 许可协议
您不能使用
Callable
来说明其他参数;它们不是通用的。您唯一的选择是说您的装饰器采用Callable
并返回一个不同的Callable
。在您的情况下,您 可以 使用 typevar 确定返回类型:
即使这样,当您使用 --- 时,生成的装饰
foo()
函数的类型签名为def (*Any, **Any) -> builtins.bool*
reveal_type()
。目前正在讨论各种提案,以使
Callable
更加灵活,但尚未实现。看Callable
以便能够指定参数名称和种类一些例子。该列表中的最后一个是包含您的特定用例的伞票,即更改可调用签名的装饰器: