![](/img/trans.png)
[英]python typing signature (typing.Callable) for function with kwargs
[英]Python inspect.Signature from typing.Callable
如何将typing.Callable
类型提示转换为inspect.Signature
function signature object?
假设我有一个 function 签名hint
:
hint = typing.Callable[[int], float]
我想将它用于类型提示以及查找符合hint
签名的函数。 为了实现后者,我可以比较相关函数中的inspect.Signature
对象,以hint
我是否有办法将类型提示转换为签名。
这是个有趣的问题。 我认为有两种可能的方法,我推荐其中一种。
Callable
构造Signature
这是可能的,但我不推荐这样做。
问题在于Callable
注释仅包含Signature
object 可以包含的信息的子集。 指定Callable
类型的不可用信息包括:
这意味着当你从一个类型构造一个Parameter
时,你必须做出很多任意的选择。
这是一个示例实现:
from collections.abc import Callable
from inspect import Parameter, Signature, signature
from typing import get_args
def callable_type_to_signature(callable_type: type) -> Signature:
params, ret = get_args(callable_type)
params = [
Parameter(f"arg{i}", Parameter.POSITIONAL_ONLY, annotation=param)
for i, param in enumerate(params)
]
return Signature(params, return_annotation=ret)
def foo(x: int, y: str, z: dict[str, bool]) -> float:
return NotImplemented
if __name__ == '__main__':
hint_foo = Callable[[int, str, dict[str, bool]], float]
sig = callable_type_to_signature(hint_foo)
print(sig)
print(signature(foo))
Output:
(arg0: int, arg1: str, arg2: dict[str, bool], /) -> float
(x: int, y: str, z: dict[str, bool]) -> float
请注意,我选择将所有参数定义为仅位置参数,并为它们命名,如argX
。
您仍然可以使用它来将一些 function 签名与此callable_type_to_signature
的 output 进行比较,但您必须注意不要将苹果与橙子进行比较。
我认为有更好的方法。
Signature
与Callable
由于您无论如何都想将签名与类型提示进行比较,因此我认为您不需要创建另一个“假” Signature
的额外步骤。 我们可以尝试直接比较这两个对象。 这是一个工作示例:
from collections.abc import Callable
from inspect import Parameter, Signature, signature
from typing import get_args, get_origin
def param_matches_type_hint(
param: Parameter,
type_hint: type,
strict: bool = False,
) -> bool:
"""
Returns `True` if the parameter annotation matches the type hint.
For this to be the case:
In `strict` mode, both must be exactly equal.
If both are specified generic types, they must be exactly equal.
If the parameter annotation is a specified generic type and
the type hint is an unspecified generic type,
the parameter type's origin must be that generic type.
"""
param_origin = get_origin(param.annotation)
type_hint_origin = get_origin(type_hint)
if (
strict or
(param_origin is None and type_hint_origin is None) or
(param_origin is not None and type_hint_origin is not None)
):
return param.annotation == type_hint
if param_origin is None and type_hint_origin is not None:
return False
return param_origin == type_hint
def signature_matches_type_hint(
sig: Signature,
type_hint: type,
strict: bool = False,
) -> bool:
"""
Returns `True` if the function signature and `Callable` type hint match.
For details about parameter comparison, see `param_matches_type_hint`.
"""
if get_origin(type_hint) != Callable:
raise TypeError("type_hint must be a `Callable` type")
type_params, return_type = get_args(type_hint)
if sig.return_annotation != return_type:
return False
if len(sig.parameters) != len(type_params):
return False
return all(
param_matches_type_hint(sig_param, type_param, strict=strict)
for sig_param, type_param
in zip(sig.parameters.values(), type_params)
)
def foo(x: int, y: str, z: dict[str, bool]) -> float:
return NotImplemented
def bar(x: dict[str, int]) -> bool:
return NotImplemented
def baz(x: list) -> bool:
return NotImplemented
if __name__ == '__main__':
hint_foo = Callable[[int, str, dict], float]
hint_bar = Callable[[dict], bool]
hint_baz = Callable[[list[str]], bool]
print(signature_matches_type_hint(signature(foo), hint_foo))
print(signature_matches_type_hint(signature(bar), hint_bar))
print(signature_matches_type_hint(signature(baz), hint_baz))
print(signature_matches_type_hint(signature(bar), hint_bar, strict=True))
Output:
True
True
False
False
细节和注意事项:
这是一个相当简单的实现。 一方面,它不处理更多“异国情调”的签名,例如包含任意关键字 arguments ( **kwargs
) 的签名。 无论如何,还不完全清楚应该如何注释。
这假设像baz
这样的更通用的 function 签名与更具体的类型提示hint_baz
不兼容。 然而,反过来,像bar
这样的更具体的function 签名与更通用的类型提示hint_bar
。
如果你只想要关于类型的精确匹配,你可以使用strict=True
。
希望这对您有所帮助并使您走上正确的道路。 也许如果我有时间,我会尝试扩展它并测试一下。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.