如何在 Python 中访问 Annotated 类型提示的元数据

11次阅读

如何在 Python 中访问 Annotated 类型提示的元数据

本文介绍如何通过 `annotated.__metadata__` 属性安全、标准地提取类型注解中携带的自定义元数据(如文档对象、验证规则等),避免字符串化陷阱,适用于生成 api 文档、运行时校验等场景。

python 3.9+ 中,typing.Annotated 提供了一种标准化方式,为类型添加任意元数据(metadata),而这些元数据并非类型本身的一部分,而是作为额外信息附着在类型上。关键在于:Annotated 类型对象暴露了 __metadata__ 属性,它是一个只读元组,包含除基础类型外的所有后续参数——这正是访问你传入的 B(x=”foo”) 实例的正确入口。

以下是一个完整、可运行的示例:

from typing import Annotated, get_args from dataclasses import dataclass import inspect  @dataclass class B:     x: str  @dataclass class A:     y: Annotated[str, B(x="foo"), "A descriptive tag"]  # 获取运行时解析后的注解字典(推荐使用 get_type_hints 或 inspect.get_annotations) annotations = inspect.get_annotations(A, eval_str=True)  # 提取 Annotated 类型 annotated_type = annotations["y"]  # ✅ 正确方式:访问 __metadata__ 属性 if hasattr(annotated_type, '__metadata__'):     metadata = annotated_type.__metadata__     print("Metadata tuple:", metadata)  # 输出: (B(x='foo'), 'A descriptive tag')      # 获取第一个元数据项(即你的 B 实例)     if metadata:         b_instance = metadata[0]         if isinstance(b_instance, B):             print("B.x =", b_instance.x)  # 输出: B.x = foo

⚠️ 注意事项:

  • __metadata__ 是 Annotated 类型对象的实例属性,仅当类型确实是 Annotated[…] 时才存在;务必先用 hasattr(…, ‘__metadata__’) 或 isinstance(…, types.GenericAlias) + 检查 __origin__ is Annotated 做安全判断;
  • inspect.get_annotations(…, eval_str=True) 是获取已求值注解的推荐方式(尤其含字符串前向引用时),但需确保模块上下文可用;
  • 不要尝试通过 str() 或 repr() 解析注解字符串——这不可靠且易受格式变更影响;
  • 若需批量处理(如框架级文档生成),建议封装工具函数:
def get_annotated_metadata(tp):     """安全提取 Annotated 元数据,返回 None 若非 Annotated 类型"""     if hasattr(tp, '__metadata__') and hasattr(tp, '__origin__') and tp.__origin__ is Annotated:         return tp.__metadata__     return None  # 使用示例 meta = get_annotated_metadata(annotations["y"]) if meta:     for item in meta:         print("Metadata item:", item)

总结:Annotated.__metadata__ 是官方支持、稳定可靠的元数据访问机制,是构建类型驱动文档、校验、序列化等高级功能的基础。始终优先使用该属性而非字符串操作或 AST 解析,以保证代码健壮性与可维护性。

立即学习Python免费学习笔记(深入)”;

text=ZqhQzanResources