Fun*_*ayu 18 python typing decorator type-hinting python-3.x
请考虑以下代码:
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
Run Code Online (Sandbox Code Playgroud)
这段代码按预期工作.现在想象一下我想扩展这个,而不是只是执行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
Run Code Online (Sandbox Code Playgroud)
我无法找出一个正确的方法来输入它.我知道在这个例子中,装饰函数和返回函数在技术上应该具有相同的签名(但即使没有检测到).
Mar*_*ers 19
你不能用Callable
任何关于其他论点的说法; 它们不是通用的.你唯一的选择是说你的装饰者需要一个,Callable
并Callable
返回一个不同的.
在您的情况下,您可以使用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:
# ...
Run Code Online (Sandbox Code Playgroud)
即使这样,生成的装饰foo()
函数也会def (*Any, **Any) -> builtins.bool*
在您使用时具有打字签名reveal_type()
.
目前正在讨论各种提案以使其Callable
更加灵活,但这些提案尚未实现.看到
举个例子.该列表中的最后一个是一个伞形票,其中包含您的特定用例,即改变可调用签名的装饰器:
与返回类型或参数混淆
对于任意函数,你根本无法做到这一点 - 甚至没有语法.这是我为它编写一些语法.
PEP 612在接受答案后被接受,我们现在在 Python 3.10 中拥有typing.ParamSpec
和typing.Concatenate
。有了这些变量,我们就可以正确键入一些操纵位置参数的装饰器。
请注意,mypy 对 PEP 612 的支持仍在进行中(跟踪问题)。
有问题的代码可以这样输入(尽管由于上述原因未在 mypy 上进行测试)
from typing import Callable, ParamSpec, Concatenate, TypeVar
Param = ParamSpec("Param")
RetType = TypeVar("RetType")
OriginalFunc = Callable[Param, RetType]
DecoratedFunc = Callable[Concatenate[Param, str], RetType]
def get_authenticated_user(): return "John"
def inject_user() -> Callable[[OriginalFunc], DecoratedFunc]:
def decorator(func: OriginalFunc) -> DecoratedFunc:
def wrapper(*args, **kwargs) -> RetType:
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 should fail
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
3684 次 |
最近记录: |