[英]Python decorator for checking the return type: Especially check for nested things like List[List[List[str]]]
我正在尝试编写我的一个 python 装饰器来进行类型检查。 它工作得很好,但在嵌套类型提示方面遇到了困难。 例如采取
@type_check
def fun(x: int) -> List[List[List[str]]]:
return [[[str(x)]]]
fun(x=42)
我有一个装饰器,它评估 function 并检查实际返回值是否具有预期的类型:
import inspect
# inside the decorator
actual_result = func(*args, **kwargs) # [[['42']]]
expected_result_type = spec.annotations['return'] # typing.List[typing.List[typing.List[str]]]
现在检查的挑战是[[['42']]]
有类型typing.List[typing.List[typing.List[str]]]
。 我怎样才能做到这一点?
我发现, [[['42']]]
只是简单地键入<class 'list'>
,它完全忽略了有关嵌套的信息。 我可以检查一下
if hasattr(expected_result_type, '__origin__'):
expected_result_type = expected_result_type.__origin__
if expected_result_type is not None:
assert isinstance(result, expected_result_type)
else: # None is kind of a special case
assert result is expected_result_type
这适用于“外层”,但忽略嵌套类型提示。 有什么方法可以检查所有图层吗?
简单的答案是:
简单地递归到所有 arguments 像:
def type_check(value, annotation):
if isinstance(annotation, type):
return isinstance(value, annotation)
elif annotation == typing.T or annotation == typing.Any:
return True
elif isinstance(annotation, typing._GenericAlias):
if annotation.__origin__ == list:
if not isinstance(value, list):
return False
inner_annotation = annotation.__args__[0]
return all(type_check(val, inner_annotation) for val in value)
例子
>>> type_check(5, int)
True
>>> type_check([1, 2, 3], typing.List[int])
True
>>> type_check([1, 2, 3], typing.List[float])
False
你可以简单地在你的装饰器中使用它。 它可能会改进以处理 Dict、Tuple 和其他常见类型。
然而,这只是一个玩具示例,如果你想进行健壮的动态类型检查,你将不得不处理Generator
、 Callable
、 Iterator
等......这不仅非常复杂,而且在某些情况下完全不可能(例如 Iterators 可以'除非迭代,否则不会被检查)。
看看enforce 项目,它就是这样:使用装饰器的python 动态类型检查。 typing
模块的源代码读起来也很有趣,但确实突破了动态自省的极限。
希望能帮助到你;)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.