
本文介绍如何通过 `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免费学习笔记(深入)”;










