Pydantic 类型系统深度解析:从基础类型到自定义类型
pydantic 项目地址: https://gitcode.com/gh_mirrors/pyd/pydantic
引言
Pydantic 是一个强大的 Python 数据验证和设置管理库,其核心功能之一就是类型系统。本文将深入探讨 Pydantic 的类型系统,包括标准库类型、严格类型、类型转换机制以及如何创建自定义类型。
标准库类型支持
Pydantic 优先使用 Python 标准库中的类型来定义字段,这使得学习曲线更加平缓。支持的标准库类型包括但不限于:
- 基本类型:int, float, str, bool
- 容器类型:list, dict, tuple, set
- 日期时间类型:datetime.date, datetime.time, datetime.datetime
- 其他:enum.Enum, pathlib.Path 等
这种设计使得熟悉 Python 标准库的开发者能够快速上手 Pydantic。
类型转换机制
Pydantic 在验证过程中能够将数据强制转换为期望的类型,提供了两种转换模式:
-
宽松模式(Lax Mode):允许从兼容类型进行自动转换
- 字符串 "123" 可以转换为整数 123
- 整数 1 可以转换为布尔值 True
-
严格模式(Strict Mode):只接受与目标类型完全匹配的值
- 字符串 "123" 不能转换为整数
- 整数 1 不能转换为布尔值
严格类型
Pydantic 提供了一系列严格类型,用于精确控制类型验证:
- StrictBool
- StrictBytes
- StrictFloat
- StrictInt
- StrictStr
这些类型只会在验证值与相应类型完全匹配(或是其子类型)时通过验证。例如:
from pydantic import StrictInt, ValidationError
try:
StrictInt(True) # 会失败,因为 bool 虽然是 int 的子类,但在严格模式下不被接受
except ValidationError as e:
print(e)
约束类型
Pydantic 还提供了一系列约束类型,可以结合复杂的验证规则使用:
- conbytes() - 带约束的字节类型
- condate() - 带约束的日期类型
- condecimal() - 带约束的十进制数
- confloat() - 带约束的浮点数
- conint() - 带约束的整数
- constr() - 带约束的字符串
这些约束类型可以指定各种验证条件,如最小值、最大值、正则表达式等。
自定义类型
当内置类型不能满足需求时,Pydantic 提供了多种创建自定义类型的方式。
使用 Annotated 组合类型
Python 3.9+ 引入了 Annotated
类型,Pydantic 利用它来创建带有额外验证逻辑的类型:
from typing_extensions import Annotated
from pydantic import Field, TypeAdapter
# 定义只接受正整数的类型
PositiveInt = Annotated[int, Field(gt=0)]
ta = TypeAdapter(PositiveInt)
print(ta.validate_python(42)) # 通过
print(ta.validate_python(-1)) # 抛出验证错误
添加验证和序列化逻辑
我们可以为自定义类型添加验证和序列化逻辑:
from typing_extensions import Annotated
from pydantic import AfterValidator, PlainSerializer
TruncatedFloat = Annotated[
float,
AfterValidator(lambda x: round(x, 2)), # 验证时保留2位小数
PlainSerializer(lambda x: f"{x:.2f}"), # 序列化为2位小数字符串
]
泛型支持
自定义类型也可以支持泛型:
from typing import TypeVar, List
from annotated_types import Gt
from typing_extensions import Annotated, TypeAliasType
T = TypeVar('T')
PositiveList = TypeAliasType('PositiveList', List[Annotated[T, Gt(0)]])
# 可以创建 PositiveList[int], PositiveList[float] 等
高级自定义:get_pydantic_core_schema
对于更复杂的自定义需求,可以实现 __get_pydantic_core_schema__
方法:
class Username(str):
@classmethod
def __get_pydantic_core_schema__(cls, source_type, handler):
# 自定义验证逻辑
def validate(value):
if not value.isalnum():
raise ValueError("只能包含字母和数字")
return cls(value)
return core_schema.no_info_plain_validator_function(validate)
这种方法提供了最大的灵活性,可以完全控制验证和序列化过程。
处理第三方类型
Pydantic 也能很好地处理第三方库定义的类型:
class ThirdPartyType:
"""假设这是第三方库定义的类型"""
def __init__(self, value):
self.value = value
class ThirdPartyTypeAdapter:
@classmethod
def __get_pydantic_core_schema__(cls, source_type, handler):
def validate(value):
if isinstance(value, ThirdPartyType):
return value
return ThirdPartyType(value)
return core_schema.no_info_plain_validator_function(validate)
PydanticThirdPartyType = Annotated[ThirdPartyType, ThirdPartyTypeAdapter]
最佳实践
- 优先使用标准库类型:除非有特殊需求,否则应优先使用标准库类型
- 合理使用严格模式:在需要精确类型控制的场景使用严格类型
- 适度自定义:从简单的 Annotated 开始,只在必要时使用高级自定义方法
- 考虑性能:复杂的自定义验证逻辑可能影响性能,应在关键路径上谨慎使用
总结
Pydantic 的类型系统既强大又灵活,从简单的标准库类型到复杂的自定义类型都能很好地支持。理解这些类型机制可以帮助开发者构建更健壮的数据模型和验证逻辑。无论是简单的字段验证还是复杂的数据转换,Pydantic 都提供了合适的工具来实现需求。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考