[英]python typing signature (typing.Callable) for function with kwargs
[英]Python Typing: Copy `**kwargs` from on function to another
这是 Python 扩展函数中的常见模式,并使用**kwargs
将所有关键字 arguments 传递给扩展的 function。
即采取
class A:
def bar(self, *, a: int, b: str, c: float) -> str:
return f"{a}_{b}_{c}"
class B:
def bar(self, **kwargs):
return f"NEW_{super().bar(**kwargs)}"
def base_function(*, a: int, b: str, c: float) -> str:
return f"{a}_{b}_{c}"
def extension(**kwargs):
return f"NEW_{super().bar(**kwargs)}"
现在调用extension(no_existing="a")
会导致TypeError
,这可以被 static 类型检查器检测到。
在运行代码之前,如何注释我的extension
以检测此问题?
此注释也有助于 IDE 为我提供正确的extension
建议。
PEP 612引入了ParamSpec
(参见 文档)类型。
我们可以利用它来生成一个装饰器,它告诉我们的类型检查器,装饰函数与给定的 function 具有相同的 arguments:
from typing import Callable, ParamSpec, TypeVar, cast, Any, Type
# Our test function
def source_func(*, foo: str, bar: int) -> str:
return f"{foo}_{bar}"
# Define some specification, see documentation
P = ParamSpec("P")
T = TypeVar("T")
# For a help about decorator with parameters see
# https://stackoverflow.com/questions/5929107/decorators-with-parameters
def copy_kwargs(kwargs_call: Callable[P, Any], target: Type[T]) -> Callable[[Callable], Callable[P, T]]:
"""Decorator does nothing but returning the casted original function"""
def return_func(func: Callable[..., T]) -> Callable[P, T]:
return cast(Callable[P, T], func)
return return_func
@copy_kwargs(source_func, float)
def kwargs_test(**kwargs) -> float:
print(source_func(**kwargs))
return 1.2
# define some expected return values
okay: float
broken_kwargs: float
broken_return: str
okay = kwargs_test(foo="a", bar=1)
broken_kwargs = kwargs_test(foo=1, bar="2")
broken_return = kwargs_test(foo="a", bar=1)
使用pyre检查此文件会给出正确的警告:
ƛ Found 3 type errors!
src/kwargs.py:30:28 Incompatible parameter type [6]: In anonymous call, for 1st parameter `foo` expected `str` but got `int`.
src/kwargs.py:30:35 Incompatible parameter type [6]: In anonymous call, for 2nd parameter `bar` expected `int` but got `str`.
src/kwargs.py:31:0 Incompatible variable type [9]: broken_return is declared to have type `str` but is used as type `float`.
MyPy最近(2022 年 4 月 7 日)合并了我尚未检查的 ParamSpec 的第一个实现。
根据相关的 typedshed Issue,PyCharm 应该支持ParamSpec
但没有正确检测到复制的**kwargs
而是抱怨okay = kwargs_test(foo="a", bar=1)
会无效 arguments。
相关问题:
基于@kound 的回答。
为了保持 DRY,我们可以在不重新声明返回类型的情况下做同样的事情。 稍后会推导类型变量T
(不是在调用copy_kwargs
时,而是在其返回的 function 时),但它不影响进一步的类型检查。
from typing import Callable, ParamSpec, TypeVar, cast, Any
# Our test function
def source_func(*, foo: str, bar: int) -> str:
return f"{foo}_{bar}"
# Define some specification, see documentation
P = ParamSpec("P")
T = TypeVar("T")
# For a help about decorator with parameters see
# https://stackoverflow.com/questions/5929107/decorators-with-parameters
def copy_kwargs(kwargs_call: Callable[P, Any]) -> Callable[[Callable[..., T]], Callable[P, T]]:
"""Decorator does nothing but returning the casted original function"""
def return_func(func: Callable[..., T]) -> Callable[P, T]:
return cast(Callable[P, T], func)
return return_func
@copy_kwargs(source_func)
def kwargs_test(**kwargs) -> float:
print(source_func(**kwargs))
return 1.2
reveal_type(kwargs_test(foo="a", bar=1))
reveal_type(kwargs_test(foo=1, bar="2"))
这是mypy playground 链接,可以实际查看它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.