Python中如何在运行时检查type annotations?
对 Python 的 annotations 一直耿耿于怀,有些时候指定类型还是很便利的。
PEP 484 -- Type Hints 中有一段:
This PEP aims to provide a standard syntax for type annotations, opening up
Python code to easier static analysis and refactoring, potential runtime type
checking, and (perhaps, in some contexts) code generation utilizing type
information.
其中提到了 potential runtime type checking, 问题就在这,如何才能实现运行时检查类型。
(碎碎念:说来也怪,高中那年看到 python 不需要指定类型,一度震惊无比,
这么写了几年 python 后,觉得有的地方还是指定类型更合适)
Python中如何在运行时检查type annotations?
你的碎碎念是对的。
import inspect
import typing
from types import GenericAlias
def check_type_annotations(obj):
"""
检查对象(函数或类)的类型注解
Args:
obj: 要检查的函数或类
Returns:
dict: 包含参数类型和返回类型信息的字典
"""
# 获取类型注解
annotations = typing.get_type_hints(obj)
# 获取函数签名
sig = inspect.signature(obj)
result = {
'name': obj.__name__ if hasattr(obj, '__name__') else str(obj),
'annotations': annotations,
'parameters': {}
}
# 检查参数注解
for param_name, param in sig.parameters.items():
param_info = {
'name': param_name,
'annotation': param.annotation,
'has_annotation': param.annotation != inspect.Parameter.empty,
'resolved_type': annotations.get(param_name, None)
}
result['parameters'][param_name] = param_info
# 检查返回类型注解
return_annotation = sig.return_annotation
result['return'] = {
'annotation': return_annotation,
'has_annotation': return_annotation != inspect.Signature.empty,
'resolved_type': annotations.get('return', None)
}
return result
def validate_runtime_type(value, expected_type):
"""
在运行时验证值是否符合类型注解
Args:
value: 要验证的值
expected_type: 期望的类型
Returns:
bool: 是否符合类型注解
"""
# 处理特殊类型
if expected_type is typing.Any:
return True
# 处理泛型别名(Python 3.9+)
if isinstance(expected_type, GenericAlias):
return isinstance(value, expected_type.__origin__)
# 处理Union类型
if hasattr(expected_type, '__origin__') and expected_type.__origin__ is typing.Union:
return any(validate_runtime_type(value, t) for t in expected_type.__args__)
# 处理List、Dict等泛型
if hasattr(expected_type, '__origin__'):
origin = expected_type.__origin__
if origin is list:
if not isinstance(value, list):
return False
# 检查列表元素类型
item_type = expected_type.__args__[0]
return all(validate_runtime_type(item, item_type) for item in value)
elif origin is dict:
if not isinstance(value, dict):
return False
# 检查键值类型
key_type, val_type = expected_type.__args__
return all(
validate_runtime_type(k, key_type) and validate_runtime_type(v, val_type)
for k, v in value.items()
)
# 标准类型检查
try:
return isinstance(value, expected_type)
except TypeError:
# 处理不可实例化的类型(如ForwardRef)
return True
# 使用示例
def example_func(name: str, age: int, scores: list[int]) -> dict[str, float]:
"""示例函数"""
return {name: float(age)}
class ExampleClass:
def __init__(self, data: dict[str, typing.Any]):
self.data = data
if __name__ == "__main__":
# 检查函数注解
func_info = check_type_annotations(example_func)
print("函数注解信息:")
print(func_info)
# 运行时类型验证
test_value = "hello"
expected_type = str
is_valid = validate_runtime_type(test_value, expected_type)
print(f"\n验证 '{test_value}' 是否为 {expected_type}: {is_valid}")
# 验证列表泛型
test_list = [1, 2, 3]
list_type = list[int]
is_list_valid = validate_runtime_type(test_list, list_type)
print(f"验证列表类型: {is_list_valid}")
用typing.get_type_hints()和inspect.signature就能搞定类型检查。
用 assert ? 所以现在我又想用回编译型静态类型语言.
你需要 mypy
you need mypy+1
运行时检查,inspect 库可以帮忙
我现在就是这么用的 写了几百行 assert 终于感觉我自己太 sb 了
感谢!
愿闻其详
此外,我还搜到了 enforce 和 typeguard 这俩玩意,不过上班没时间研究,有玩过的朋友可以讲讲吗?
https://github.com/RussBaz/enforce
https://github.com/agronholm/typeguard
什么时候出个 TypePython 就完美了。
484 里面已经说了,annotation 是函数 signature 的一部分,inspect 是可以获取的
可以看看文档
https://docs.python.org/3/library/inspect.html#inspect.getfullargspec
而且楼上建议的 mypy 还是略废材,可以试试
https://github.com/google/pytype
我两年前写过一个运行时检查类型的: https://github.com/huntzhan/magic-constraints
不过我认为在 Python 里做类型检查价值不大
首先设计上要避免需要参数检查
一定要检查的的地方用装饰器模式实现会让代码简洁阅读性更好

