获取类中所有声明或类型化的参数

获取类中所有声明或类型化的参数

本文旨在提供一种使用 python 反射机制获取类及其父类中所有声明或类型化的参数的方法。通过结合 inspect.get_annotations 和类的 __mro__ 属性,我们可以提取包括父类在内的所有类型注解,并将其组织成一个易于使用的字典。本文提供详细的代码示例和解释,帮助读者理解和应用这一技术。

使用 inspect.get_annotations 和 __mro__ 获取参数类型信息

Python 的 inspect 模块提供了强大的反射功能,其中 inspect.get_annotations 函数可以获取指定对象的类型注解。类的 __mro__ 属性则包含了类的继承关系,即方法解析顺序(Method Resolution Order)。结合这两个工具,我们可以遍历类的继承链,提取所有父类的类型注解。

以下是一个示例代码:

import inspect from typing import Any, Optional, Dict  class Parent:     parent_param: str     parent_default: Optional[str] = None      @classmethod     def find_params_meta(cls) -> Dict[str, Any]:         params_meta = {}         for c in reversed(cls.__mro__):             if hasattr(c, '__annotations__'):                 for k, v in inspect.get_annotations(c).items():                     if k not in params_meta: # 避免子类覆盖父类同名参数的类型信息                         params_meta[k] = {"types": [v]}                         if hasattr(cls, k) and getattr(cls, k) != inspect._empty:                             params_meta[k]["default"] = getattr(cls, k)         return params_meta   class Child(Parent):     child_param: str     _params_meta: Optional[Dict[str, Any]] = None      def __init__(self, parent_param: str, child_param: str):         self._params_meta = self.find_params_meta()         self.child_param = child_param         self.parent_param = parent_param      def __getattr__(self, name):         # 模拟未初始化的属性访问         raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")  # 实例化对象并打印结果 child_instance = Child("parent param", "child param") print(child_instance._params_meta)  # 断言 expected_result = {     "parent_param": {"types": [str]},     "parent_default": {"types": [str, type(None)], "default": None},     "child_param": {"types": [str]}, }  assert child_instance._params_meta == expected_result

代码解释:

  1. Parent 类和 Child 类: 定义了父类 Parent 和子类 Child,包含类型注解的类属性。
  2. find_params_meta 方法: 这是一个类方法,用于获取类及其父类的类型注解。
  3. cls.__mro__: __mro__ 属性返回一个包含类及其所有父类的元组,按照方法解析顺序排列。 使用 reversed 反转这个顺序,确保子类的类型信息优先。
  4. inspect.get_annotations(c): 获取每个类 c 的类型注解,返回一个字典。
  5. 类型信息合并: 遍历父类链时,将父类的类型信息合并到 params_meta 中。如果子类定义了与父类同名的参数,则子类的类型信息优先。
  6. 默认值获取: 检查类属性是否有默认值,如果有则添加到结果字典中。
  7. __getattr__ 方法: 为了模拟问题描述中“未实例化属性”的场景,重写了 __getattr__ 方法,当访问不存在的属性时抛出 AttributeError。

运行结果:

{'parent_param': {'types': [<class 'str'>]}, 'parent_default': {'types': [<class 'str'>, <class 'NoneType'>], 'default': None}, 'child_param': {'types': [<class 'str'>]}

注意事项

  • 类型覆盖: 在继承关系中,如果子类重新定义了父类的属性,并且类型注解不同,则子类的类型注解会覆盖父类的类型注解。
  • 性能: 使用反射可能会影响性能,特别是在频繁调用的场景中。 可以考虑缓存结果以提高性能。
  • 兼容性: inspect.get_annotations 是 Python 3.9 引入的,如果需要兼容更早的版本,可以使用 typing.get_type_hints。

总结

通过结合 inspect.get_annotations 和 __mro__ 属性,我们可以方便地获取类及其父类的类型注解。这对于实现一些通用的工具函数,例如自动生成 API 文档、参数校验等非常有用。 在实际应用中,需要注意类型覆盖、性能和兼容性等问题。

© 版权声明
THE END
喜欢就支持一下吧
点赞10 分享