Python: Annotated和Pydantic
Annotated
typing.Annotated
在 Python3.9 版中引入,用于为类型注解添加元数据。作为 PEP 593 -- Flexible function and variable annotations
的一部分被添加到 typing 模块中。使用 Annotated
,Python
环境必须是 3.9 或更高版本。
typing.Annotated
的核心作用是为现有的 Python 类型添加元数据(metadata)
。它本身并不执行任何运行时的类型检查或验证。可以将
Annotated
看作是一个类型装饰器
,它允许你将额外的信息(可以是任何 Python 对象) 与一个类型关联起来。
这个设计的主要目的是为了让第三方工具能够更好地理解类型注解,从而提供更丰富的功能,例如类型检查、文档生成等,且无需修改 Python 的核心类型系统。通过使用 Annotated
,开发者可以更灵活地表达类型信息,使代码更加清晰和可维护,
Pydantic
Pydantic 是一个 Python 库,主要用于数据验证和解析。它通过 Python 的类型提示 (type hints) 来定义数据的结构,并在运行时验证数据的有效性。
以下是 Pydantic 的主要用途和优点:
-
数据验证 (Data Validation)
: 这是 Pydantic 最核心的功能。你可以使用 Pydantic 模型清晰地定义预期的数据结构和类型。当接收到外部数据(例如,来自 API 请求、配置文件、用户输入等)时,Pydantic 会自动根据你定义的模型进行验证。如果数据不符合预期类型或约束,Pydantic 会抛出详细的错误信息,告诉你哪些字段出了问题以及具体原因。 -
数据解析 (Data Parsing)
: 除了验证,Pydantic 还能将输入数据解析成 Python 对象。即使输入数据是字符串、数字或其他格式,Pydantic 也会尝试将其转换为模型中定义的 Python 类型。例如,一个字符串 “123” 可以被解析成 int 类型。 -
数据序列化 (Data Serialization): Pydantic 模型可以将 Python 对象序列化为其他格式,如 JSON。这对于构建 API 接口非常有用,因为你需要将 Python 对象转换为 JSON 格式发送给客户端。
-
类型提示的强大利用: Pydantic 充分利用了 Python 3.6+ 引入的类型提示。通过类型提示,你可以清晰地表达数据的预期结构,这不仅方便了 Pydantic 的验证和解析,也提高了代码的可读性和可维护性,并能与 MyPy 等静态类型检查工具良好集成。
-
JSON Schema 生成: Pydantic 可以根据你的模型自动生成 JSON Schema。JSON Schema 是一种描述 JSON 数据结构的标准化方式,这对于 API 文档生成和与其他系统进行数据交互非常有用。
-
与 Web 框架集成: Pydantic 与许多流行的 Python Web 框架(如 FastAPI、Starlette)无缝集成。FastAPI 甚至在底层 heavily rely on Pydantic 来处理请求和响应的数据验证和序列化。
-
自定义验证器 (Custom Validators): 除了基本的类型验证,Pydantic 还允许你定义自定义的验证逻辑,以满足更复杂的业务规则。你可以编写函数来检查字段的值是否符合特定的要求。
-
数据清洗和转换 (Data Cleaning and Transformation): 在验证过程中,Pydantic 还可以对数据进行清洗和转换。例如,你可以定义将字符串自动去除首尾空格,或者将日期字符串转换为 datetime 对象。
总而言之,Pydantic 主要用于确保你的 Python 应用程序接收和处理的数据是有效和符合预期的。它可以帮助你减少因数据格式错误而引发的 bug,提高代码的健壮性和可靠性,并简化数据处理相关的开发工作。
一些常见的应用场景包括:
- API 开发: 验证和解析 API 请求的输入数据,以及序列化 API 的输出数据。
- 数据处理管道: 确保从不同来源读取的数据符合预期的格式和类型。
- 配置文件解析: 加载和验证应用程序的配置文件。
- 用户输入验证: 验证 Web 表单或命令行工具接收到的用户输入。
- 机器学习模型的数据预处理: 确保输入模型的数据具有正确的结构和类型。
总结:
- typing.Annotated: 是 Python 类型提示系统的一部分,提供了一种为现有类型添加元数据的标准方法。它本身不执行任何运行时操作,而是让第三方工具能够读取和利用这些额外的元数据。
- Pydantic: 专注于数据建模、验证、解析和序列化,通过定义 Pydantic 模型并利用类型提示来实现这些功能。它是一个功能强大的库,用于处理外部数据并确保其符合预期的结构和类型。
在实际应用中,Annotated 经常和 Pydantic 组合使用,以增强 Pydantic 模型的功能。虽然 Pydantic 也可以单独使用,但结合 Annotated 可以提供更灵活和标准化的方式来定义模型的行为。
单独使用 Pydantic 的情况:
基本的数据验证和解析: 当你只需要定义数据的基本结构和类型,并进行简单的验证和解析时,可以直接使用 Pydantic 模型。例如:
BaseModel
from pydantic import BaseModel
class User(BaseModel):
id: int
name: str
signup_ts: float | None = None
在这种情况下,Pydantic 会根据类型提示自动进行类型检查和基本的数据转换。
Field
函数参数和返回值注释 (使用 Field
):
from pydantic import Field
def search_database(
query: str = Field(description="Search query string"),
limit: int = Field(10, description="Maximum number of results", ge=1, le=100)
) -> list:
...
与这种方式相比,如果只需要需要定义简单的数据结构,并且不需要复杂的验证或序列化,可以直接使用 Python 的类型提示:
def search_database(
query: str,
limit: int,
) -> list:
...
- 优点:
- 结构化数据:
Field
来自pydantic
或类似的库,它允许你定义更丰富的数据类型信息,例如描述、默认值、验证规则(ge
,le
)。 - 自动化文档和验证: 这些信息可以被用于自动生成 API 文档(例如使用 FastAPI),以及在运行时进行数据验证。
- IDE 支持: 现代 IDE 可以利用这些信息提供更好的代码提示和错误检查。
- 结构化数据:
- 缺点:
- 依赖外部库: 你需要引入
pydantic
或类似的库。 - 更冗长: 代码量相对较多,对于简单的类型注释可能显得过于繁琐。
- 依赖外部库: 你需要引入
Annotated
和 Field
结合使用
from typing import Annotated
from pydantic import Field
def analyze_metrics(
# number with range constraints
count: Annotated[int, Field(description="数据数量", ge=1, le=100)],
ratio: Annotated[float, Field(description="比率", ge=0, le=1)],
# String with pattern and length constraints
user_id: Annotated[
str, Field(description="用户ID 格式: XX0000", pattern=r"^[a-z]{2}\d{4}$")
],
comment: Annotated[str, Field(description="备注", min_len=3, max_length=500)] = "",
factor: Annotated[float, Field(description="因子", multiple_of=5)] = 10,
) -> dict[str, float]:
"""分析一组数据的统计信息."""
pass
- 优点:
- 类型提示和元数据: 使用
Annotated
可以将类型提示与元数据(例如Field
)结合起来。 - 灵活性: 允许你添加多个元数据,不仅限于描述和验证规则。
- 标准化:
Annotated
是 Python 3.9 引入的标准库typing
的一部分,因此不需要额外的依赖(除非Field
来自外部库)。
- 类型提示和元数据: 使用
- 缺点:
- 可读性略差: 相对于简单的类型提示,
Annotated
的语法可能稍微复杂一些。 - 需要 Python 3.9+:
Annotated
是 Python 3.9 引入的特性。
- 可读性略差: 相对于简单的类型提示,
选择策略
Annotated 通常与 Pydantic 结合使用,以增强 Pydantic 模型字段的定义,添加自定义验证逻辑、元数据等。
-
基本数据建模: 如果你只需要定义简单的数据结构,并且不需要复杂的验证或序列化,可以直接使用 Python 的类型提示。
-
数据验证和解析: 如果你需要对输入数据进行严格的验证和解析,并将其转换为 Python 对象,选择 Pydantic。
-
自定义验证和元数据: 如果你需要在 Pydantic 模型中添加自定义的验证逻辑或额外的元数据(例如字段描述、别名、格式等),那么使用 Annotated 结合 Pydantic 是推荐的做法。
-
第三方库集成: 如果你使用的第三方库支持 Annotated,并且你想利用类型提示中的元数据来配置这些库的行为,那么可以使用 Annotated。例如,Pydantic 利用 Annotated 来支持自定义验证器 (PlainValidator) 和字段配置 (Field).