Python-包-pydantic-数据模型定义和字段校验

介绍

Pydantic 可以帮助我们显示的定义数据集中每个字段的数据类型,并在数据导入时,对数据进行强制验证,以确保输出的数据满足预期,并在数据无效时提供用户友好的错误信息。
pydantic主要是一个解析库,而不是验证库。验证是达到目的的一种手段:构建符合所提供的类型和约束的模型。换句话说,pydantic保证输出模型的类型和约束,而不是输入数据。
官方文档

其实python对于变量本身是提供了类型限制的方案的,但由于约束不是强制的,所以可能我们会出现偷懒的情况~,也许大部分情况下可能只是影响我们二次阅读代码或者函数调用的成本(回溯代码核查输入输出格式),但是在一些特殊的情境下,可能会导致我们的结果出现一些意料之外的错误,示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
# def sum(a,b):
def sum_1(a: int, b: int):
print(str(a) +"+" + str(b) + " = " + str(a+b))
return a + b

def sum_2(a, b):
print(str(a) +"+" + str(b)+" = "+ str(a+b))
return a + b

a=1;b=2; sum_1(a,b) # 1+2 = 3
a=1;b=2; sum_2(a,b) # 1+2 = 3
a=str(1);b=str(2); sum_2(a,b) # 1+2 = 12

我们相对两个数字求和,但是在定义函数的时候,忘记了对输入参数的类型进行限制,而某个调用的时候,可能会由于一些特殊的需求,我们把数字都格式成了字符串,这个时候我们如果函数使用sum_2的方式,那么就会出现一些无法预测的问题。
当然上述示例,主要是对输入输出类型进行了限制,但是有时候,我们除了限制输入输出,还需要对数据的具体结构进行限制,比如某些函数我们处理转录本为 “NM“的数据,而不应该出现 “NR“格式的数据等。所以除了数据类型,有时候,我们还需要惊醒一些更复杂的数据格式校验,这个时候,我们就可以使用pydantic来帮我们进行。

主要功能

Pydantic 的核心功能包括:

  • 数据验证:确保输入数据符合预定义的类型和结构。
  • 序列化 :将复杂的数据结构转换为 Python 数据类型,便于处理和传输。
  • 错误处理:提供详细的错误信息,帮助开发者快速定位和修复问题。
  • 配置管理:支持通过环境变量等方式管理配置,提高应用的可配置性。

特性

Pydantic 以其高效、灵活和易用性著称,以下是其主要特性:

  • 类型注解支持:Pydantic 充分利用 Python 的类型注解,使得数据模型的定义简洁明了。
  • 高性能:Pydantic 的核心验证逻辑是用 Rust 编写的,这使得它在数据验证方面表现出色,速度快于许多其他 Python 数据验证库。
  • JSON Schema 生成:Pydantic 模型可以自动生成 JSON Schema,便于与其他工具和系统集成。
  • 严格模式和宽松模式:Pydantic 支持严格模式(strict=True)和宽松模式(strict=False),在严格模式下,数据不会被自动转换,而在宽松模式下,Pydantic 会尝试将数据转换为正确的类型。
  • 数据类支持:Pydantic 支持标准库中的数据类(dataclasses)和 TypedDict,提供更灵活的数据结构定义。
  • 自定义验证器和序列化器:Pydantic 允许开发者自定义验证器和序列化器,以满足特定的数据处理需求。
  • 生态系统丰富:Pydantic 被广泛应用于各种项目中,包括 FastAPI、Hugging Face、Django Ninja、SQLModel 和 LangChain 等知名库。
  • 经过实战测试:Pydantic 每月被下载超过 7000 万次,被所有 FAANG 公司和纳斯达克 25 家最大公司中的 20 家使用,证明了其可靠性和广泛的应用场景。

通过这些特性,Pydantic 为 Python 开发者提供了一个强大而灵活的工具,用于处理数据验证和解析,极大地简化了数据处理的复杂性。

pydantic的使用

安装

1
2
3
4
# pip 直接安装
pip install pydantic
# 基于conda安装
conda install -c conda-forge pydantic

Pydantic 可以可选的使用 Cython 进行编译,将会带来 30%-50%的性能提升。

PyPI可以为Linux、MacOS和64位Windows提供二进制文件。如果您是手动安装,请在安装pydantic之前安装 cython,这样编译就会自动进行。

要测试pydantic 是否已经编译,可以使用如下方法:

1
2
>>> import pydantic
>>> print(pydantic.compiled)

使用

定义数据模型

在使用Pydantic进行数据验证之前,首先需要定义数据模型。Pydantic通过继承BaseModel类来实现这一点。以下是一个简单的示例,展示了如何定义一个包含基本数据类型的数据模型。

1
2
3
4
5
6
7
8
from pydantic import BaseModel
from typing import List

class EmployeeModel(BaseModel):
name: str
username: str
salary: int
habits: List[str]

在这个示例中,我们定义了一个名为 EmployeeModel 的类,它继承自 BaseModel 。该类包含四个字段:nameusernamesalaryhabits。每个字段都通过类型注解(type annotation)指定了其数据类型。

数据验证示例

Pydantic的核心功能之一是数据验证。一旦定义了数据模型,Pydantic会自动验证输入数据是否符合模型定义的类型和结构。以下是一个验证示例:

1
2
employee = EmployeeModel(name='Bar', username='bar', salary=1000, habits=[])
print(employee)

在这个示例中,我们创建了一个EmployeeModel的实例,并传入相应的字段值。Pydantic会自动验证这些值是否符合模型定义的类型。如果验证通过,实例将被成功创建。

如果输入数据不符合模型定义的类型,Pydantic会抛出 ValidationError 异常,并提供详细的错误信息。例如:

1
2
3
4
try:
employee = EmployeeModel(name='Bar', username='bar', salary='secret', habits=[])
except ValidationError as e:
print(e)

在这个示例中,我们将salary字段设置为字符串’secret’,这不符合模型定义的int类型。因此,Pydantic会抛出ValidationError异常,并显示详细的错误信息:

1
2
3
1 validation error for EmployeeModel
salary
value is not a valid integer (type=type_error.integer)

处理外部数据

Pydantic 不仅可以在创建模型实例时进行数据验证,还可以处理外部数据,如JSON格式的数据。以下是一个处理外部数据的示例:

1
2
3
4
5
6
7
8
import json
# 假设我们有一个JSON字符串
json_data = '{"name": "Bar", "username": "bar", "salary": "1000", "habits": []}'
# 将JSON字符串解析为Python字典
data = json.loads(json_data)
# 使用字典数据创建模型实例
employee = EmployeeModel(**data)
print(employee)

在这个示例中,我们首先将JSON字符串解析为Python字典,然后使用字典数据创建EmployeeModel的实例。Pydantic会自动验证字典中的数据是否符合模型定义的类型。即使salary字段在JSON字符串中是字符串类型,Pydantic也会尝试将其转换为整数类型。

通过这种方式,Pydantic可以方便地处理来自外部数据源的数据,并确保数据的完整性和一致性。

高级功能

自定义验证器

Pydantic 提供了强大的数据验证功能,其中自定义验证器是一个非常重要的特性。通过自定义验证器,开发者可以实现复杂的验证逻辑,确保数据的完整性和准确性。

使用 @validator 装饰器

Pydantic 提供了 @validator 装饰器,用于在模型字段上定义自定义验证逻辑。以下是一个简单的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pydantic import BaseModel, validator, ValidationError

class UserModel(BaseModel):
name: str
age: int

@validator('name')
def name_must_contain_space(cls, v):
if ' ' not in v:
raise ValueError('must contain a space')
return v.title()

@validator('age')
def age_must_be_positive(cls, v):
if v <= 0:
raise ValueError('must be a positive integer')
return v

try:
user = UserModel(name="john doe", age=-1)
except ValidationError as e:
print(e)

在这个示例中,我们定义了两个验证器:

  1. name_must_contain_space:确保 name 字段包含空格。
  2. age_must_be_positive:确保 age 字段是正整数。

验证器的参数

@validator 装饰器支持多个参数,用于控制验证器的行为:

  • pre: 如果设置为 True,验证器将在字段验证之前执行。
  • each_item: 如果设置为 True,验证器将应用于列表或字典中的每个元素。
  • always: 如果设置为 True,验证器将始终执行,即使字段没有值。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    from pydantic import BaseModel, validator

    class UserModel(BaseModel):
    name: str
    hobbies: list

    @validator('hobbies', each_item=True)
    def check_hobbies_not_empty(cls, v):
    if not v:
    raise ValueError('hobby cannot be empty')
    return v

在这个示例中,check_hobbies_not_empty 验证器将应用于 hobbies 列表中的每个元素,确保每个爱好不为空。

嵌套模型

Pydantic 支持嵌套模型,这使得定义复杂的数据结构变得非常方便。嵌套模型可以包含其他模型作为字段,从而实现层次化的数据结构。

定义嵌套模型

以下是一个嵌套模型的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pydantic import BaseModel

class Address(BaseModel):
street: str
city: str
state: str
zip_code: str

class User(BaseModel):
name: str
address: Address

user = User(name="John Doe", address={"street": "123 Elm St", "city": "Springfield", "state": "IL", "zip_code": "62704"})
print(user)

在这个示例中,User 模型包含一个 Address 模型作为其 address 字段。

嵌套模型的验证

嵌套模型的验证是自动进行的。如果嵌套模型的字段不符合定义的类型或验证规则,Pydantic 将抛出 ValidationError

1
2
3
4
try:
user = User(name="John Doe", address={"street": "123 Elm St", "city": "Springfield", "state": "IL", "zip_code": "invalid"})
except ValidationError as e:
print(e)

在这个示例中,zip_code 字段不符合定义的类型,因此会抛出验证错误。

配置选项

常用的配置选项

  • allow_mutation: 如果设置为 False,模型实例将是不可变的。
  • extra: 控制如何处理未定义的字段。可选值包括 allow、forbid 和 ignore。
  • validate_all: 如果设置为 True,所有字段在实例化时都将被验证。
  • error_msg_templates: 自定义错误消息模板。
pydantic:v1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pydantic import BaseModel

class User(BaseModel):
name: str
age: int

class Config: # pydantic v1版本方式,在v2版本不再支持config方式的配置
allow_mutation = False
extra = 'forbid'
validate_all = True

user = User(name="John Doe", age=30)
try:
user.age = 31 # 这将引发错误,因为 allow_mutation 设置为 False
except TypeError as e:
print(e)

上述是在 pydantic:v1 版本中的格式,在 pydantic:v2版本不再支持config方式的配置,使用示例如下:

pydantic:v2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pydantic import BaseModel

class User(BaseModel):
name: str
age: int
allow_mutation = False
extra = 'forbid'
validate_all = True

user = User(name="John Doe", age=30)
try:
user.age = 31 # 这将引发错误,因为 allow_mutation 设置为 False
except TypeError as e:
print(e)

在这个示例中,allow_mutation 设置为 False,因此 user 实例是不可变的。

自定义错误消息

可以通过 error_msg_templates 自定义错误消息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pydantic import BaseModel, validator

class User(BaseModel):
name: str
age: int
class Config:
error_msg_templates = {
'value_error.any_str.max_length': '字符串长度不能超过 {limit_value} 个字符',
'value_error.any_str.min_length': '字符串长度不能少于 {limit_value} 个字符'
}
@validator('name')
def name_length(cls, v):
if len(v) > 3:
raise ValueError('name too long')
return v

在这个示例中,我们自定义了两个错误消息模板,用于处理字符串长度的错误。

通过这些高级功能,Pydantic 提供了强大的数据验证和自定义能力,使得开发者可以轻松处理复杂的数据结构和验证逻辑。

参考资料

pydantic官方文档

-------------本文结束感谢您的阅读-------------