[英]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.