Pydantic:目前最流行的Python数据验证库

Python学习之-Pydantic详解 Pydantic 是一个 Python 库,用于数据解析和验证。它主要基于 Python 类型提示来实现数据的校验和设定限制条件。在 Pydantic 中,BaseModel 是一个核心基类,用于创建数据模型。Pydantic 的主要优势在于其结合了灵活性和强大的数据处理能力,同时简化了常规的数据校验任务,让开发人员更专注于业务逻辑的实现。它通常用于快速开发中,特别是在构建API、数据交互和设置管理场景中发挥重要作用。 阅读详情
AI摘要

本文深入探讨Pydantic库在Python中的强大用途,包括数据验证、序列化及与Flask、LangChain的集成。Pydantic简化了数据处理流程,确保数据格式正确无误。

前言

在处理来自系统外部的数据,如API、终端用户输入或其他来源时,我们必须牢记开发中的一条基本原则:“永远不要相信用户的输入”。

因此,我们必须对这些数据进行严格的检查和验证,确保它们被适当地格式化和标准化。这样做的目的是为了确保这些数据符合我们的程序所需的输入规范,从而保障项目能够正确且高效地运行。

为什么使用 Python 的 Pydantic 库?

Pydantic 是一个在 Python 中用于数据验证和解析的第三方库,它现在是 Python 使用最广泛的数据验证库。

  • 它利用声明式的方式定义数据模型和Python 类型提示的强大功能来执行数据验证和序列化,使您的代码更可靠、更可读、更简洁且更易于调试。。
  • 它还可以从模型生成 JSON 架构,提供了自动生成文档等功能,从而轻松与其他工具集成。

Pydantic 在很多优秀的项目中被广泛使用。

Pydantic 的一些主要特性

易用性

Pydantic 使用起来简单直观,需要最少的样板代码和配置。它适用于许多流行的 IDE 和静态分析工具,例如 PyCharm、VS Code、mypy 等。Pydantic 可以轻松与其他流行的 Python 库(如 Flask、Django、FastAPI 和 SQLAlchemy)集成,使其易于在现有项目中使用。


类型注解

Pydantic 使用类型注解来定义模型的字段类型,以确保确保数据符合预期的类型和格式。你可以使用Python 内置的类型、自定义类型或者其他Pydantic 提供的验证类型。


数据验证,用户友好的错误

Pydantic 自动根据模型定义进行数据验证。它会检查字段的类型、长度、范围等,并自动报告验证错误,Pydantic 会提供信息丰富且可读的错误消息,包括错误的位置、类型和输入。你可以使用 ValidationError 异常来捕获验证错误。


序列化与反序列化

Pydantic 提供了从各种数据格式(例如 JSON、字典)到模型实例的转换功能。它可以自动将输入数据解析成模型实例,并保留类型安全性和验证规则。


性能高

Pydantic 的核心验证逻辑是用 Rust 编写的,使其成为 Python 中最快的数据验证库之一。它还支持延迟验证和缓存,以提高效率。

Pydantic和内置的dataclasses非常的像,主要区别在于Pydantic拥有更加强大的数据验证和序列化功能。


安装

安装 Pydantic 非常简单:

pip install pydantic[email]  # 会用到邮箱校验,直接在这一起安装了

如何使用 Pydantic?

使用 Pydantic 的主要方法是创建继承自 BaseModel 的自定义类,这是所有 Pydantic 模型的基类。然后,您可以使用类型注释定义模型的属性,并选择性地提供默认值或验证器。

pydantic的核心是模型(Model)

例如,让我们为用户创建一个简单的模型,并使用 Python 的类型注解来声明期望的数据类型:

#! -*-conding: UTF-8 -*-
# @公众号: 海哥python
from enum import Enum

from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel, ValidationError, EmailStr
# 导入pydantic对应的模型基类
from pydantic import constr, conint


class GenderEnum(str, Enum):
    """
    性别枚举
    """
    male = "男"
    female = "女"


class User(BaseModel):
    id: int
    name: str = "小卤蛋"
    age: conint(ge=0, le=99)  # 整数范围:0 <= age <= 99
    email: EmailStr
    signup_ts: Optional[datetime] = None
    friends: List[str] = []
    password: constr(min_length=6, max_length=10)  # 字符长度
    phone: constr(pattern=r'^1\d{10}$')  # 正则验证手机号
    sex: GenderEnum  # 枚举验证, 能传: 男和女

我们定义了一个名为User的类,继承自BaseModel。

  • id属性是整型,且是必需的,表示用户ID。
  • name属性是字符串类型,默认值为’小卤蛋’。
  • age属性是整型,且是必需的,表示用户年龄。
  • email属性是电子邮件地址类型。
  • signup_ts属性是可选的日期时间类型,默认值为None,表示用户注册时间。
  • friends属性是字符串列表类型,默认值为空列表,表示用户的朋友列表。
  • sex属性是枚举类型,可选值为“男”或“女”,表示用户的性别。

验证数据

一旦你定义了模型,你可以使用它来验证数据。

如果要从字典实例化 User 对象,可以使用字典对象解包或者.model_validate()、.model_validate_json()类方法:

if __name__ == '__main__':

    user_data = {
        "id": 123,
        "name": "小卤蛋",
        "age": 20,
        "email": "xiaoludan@example.com",
        'signup_ts': '2024-07-19 00:22',
        'friends': ["公众号:海哥python", '小天才', b''],
        'password': '123456',
        'phone': '13800000000',
        'sex': '男'
    }

    try:
        # user = User(**user_data)
        user = User.model_validate(user_data)
        print(f"User id: {user.id}, User name: {user.name}, User email: {user.email}")
    except ValidationError as e:
        print(f"Validation error: {e.json()}")

都符合模型定义的情况下,您可以像往常一样访问模型的属性:

User id: 123, User name: 小卤蛋, User email: xiaoludan@example.com

如果数据不符合模型的定义(以下故意不传 id 字段),Pydantic 将抛出一个 ValidationError。

if __name__ == '__main__':

    user_data = {
        # "id": 123,
        "name": "小卤蛋",
        "age": 20,
        "email": "xiaoludan@example.com",
        'signup_ts': '2024-07-19 00:22',
        'friends': ["公众号:海哥python", '小天才', b''],
        'password': '123456',
        'phone': '13800000000',
        'sex': '男'
    }

    try:
        # user = User(**user_data)
        user = User.model_validate(user_data)
        print(f"User id: {user.id}, User name: {user.name}, User email: {user.email}")
    except ValidationError as e:
        print(f"Validation error: {e.json()}")

报错:

Validation error: [{"type":"missing","loc":["id"],"msg":"Field required","input":{"name":"小卤蛋","age":20,"email":"xiaoludan@example.com","signup_ts":"2024-07-19 00:22","friends":["公众号:海哥python","小天才",""],"password":"123456","phone":"13800000000","sex":"男"},"url":"https://errors.pydantic.dev/2.8/v/missing"}]

自定义验证

除了内置的验证器,还可以为模型定义自定义验证器。假设要确保用户年龄在18岁以上,可以使用@field_validator装饰器创建一个自定义验证器:

# ! -*-conding: UTF-8 -*-
# @公众号: 海哥python
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel, EmailStr, field_validator, ValidationError


def check_name(v: str) -> str:
    """Validator to be used throughout"""
    if not v.startswith("小"):
        raise ValueError("must be startswith 小")
    return v


class User(BaseModel):
    id: int
    name: str = "小卤蛋"
    age: int
    email: EmailStr
    signup_ts: Optional[datetime] = None
    friends: List[str] = []

    validate_fields = field_validator("name")(check_name)

    @field_validator("age")
    @classmethod
    def check_age(cls, age):
        if age < 18:
            raise ValueError("用户年龄必须大于18岁")
        return age

当尝试创建一个只有12岁的小朋友用户:

if __name__ == '__main__':
    user_data = {
        "id": 123,
        "name": "小卤蛋",
        "age": 12,
        "email": "xiaoludan@example.com",
        'signup_ts': '2024-07-19 00:22',
        'friends': ["公众号:海哥python", '小天才', b''],
    }
    try:
        user = User(**user_data)
    except ValidationError as e:
        print(f"Validation error: {e.json()}")

将得到一个错误:

Validation error: [{"type":"value_error","loc":["age"],"msg":"Value error, 用户年龄必须大于18岁","input":12,"ctx":{"error":"用户年龄必须大于18岁"},"url":"https://errors.pydantic.dev/2.8/v/value_error"}]

或者,当name不是小开头的话:

if __name__ == '__main__':
    user_data = {
        "id": 123,
        "name": "大卤蛋",
        "age": 20,
        "email": "xiaoludan@example.com",
        'signup_ts': '2024-07-19 00:22',
        'friends': ["公众号:海哥python", '小天才', b''],
    }
    try:
        user = User(**user_data)
    except ValidationError as e:
        print(f"Validation error: {e.json()}")

将得到报错:

Validation error: [{"type":"value_error","loc":["name"],"msg":"Value error, must be startswith 小","input":"大卤蛋","ctx":{"error":"must be startswith 小"},"url":"https://errors.pydantic.dev/2.8/v/value_error"}]

如果要同时动态校验多个字段,还可以使用model_validator装饰器。

# ! -*-conding: UTF-8 -*-
# @公众号: 海哥python
from datetime import datetime
from typing import List, Optional
from typing_extensions import Self  # 如果python版本不低于3.11,则可以直接从typing中导入Self
from pydantic import BaseModel, ValidationError, EmailStr, field_validator, model_validator


def check_name(v: str) -> str:
    """Validator to be used throughout"""
    if not v.startswith("小"):
        raise ValueError("must be startswith 小")
    return v


class User(BaseModel):
    id: int
    name: str = "小卤蛋"
    age: int
    email: EmailStr
    signup_ts: Optional[datetime] = None
    friends: List[str] = []

    validate_fields = field_validator("name")(check_name)

    @field_validator("age")
    @classmethod
    def check_age(cls, age):
        if age < 18:
            raise ValueError("用户年龄必须大于18岁")
        return age

    @model_validator(mode="after")
    def check_age_and_name(self) -> Self:
        if self.age < 30 and self.name != "小卤蛋":
            raise ValueError("用户年龄必须小于30岁, 且名字必须为小卤蛋")

        return self


if __name__ == '__main__':
    user_data = {
        "id": 123,
        "name": "小小卤蛋",
        "age": 20,
        "email": "xiaoludan@example.com",
        'signup_ts': '2024-07-19 00:22',
        'friends': ["公众号:海哥python", '小天才', b''],
    }
    try:
        user = User(**user_data)
        print(user.model_dump())
    except ValidationError as e:
        print(f"Validation error: {e.json()}")

执行结果:

Validation error: [{"type":"value_error","loc":[],"msg":"Value error, 用户年龄必须小于30岁, 且名字必须为小卤蛋","input":{"id":123,"name":"小小卤蛋","age":20,"email":"xiaoludan@example.com","signup_ts":"2024-07-19 00:22","friends":["公众号:海哥python","小天才",""]},"ctx":{"error":"用户年龄必须小于30岁, 且名字必须为小卤蛋"},"url":"https://errors.pydantic.dev/2.8/v/value_error"}]

validate_call也是在我看来非常有用的装饰器。

from typing import Annotated

from pydantic import BaseModel, Field, validate_call


class Person(BaseModel):
    name: str = Field(..., min_length=1, max_length=100)
    age: int = Field(..., gt=0, lt=20)


# @validate_call
def greet(person: Person, message: Annotated[str, Field(min_length=1, max_length=100)]):
    print(f"Hello, {person.name}! {message}")


# 正确的调用
greet(Person(name="公众号:海哥python", age=18), "How are you?")
greet(Person(name="公众号:海哥python", age=18), 1)

在不使用validate_call的情况下,虽然pycharm中会提示1类型不匹配,但是实际执行时并不会报错。

然而,我们通常是希望定义和使用要符合我们的预期,以避免不可预见的错误。

此时validate_call装饰器就可以很好的为我们实现这一需求。

from typing import Annotated

from pydantic import BaseModel, Field, validate_call


class Person(BaseModel):
    name: str = Field(..., min_length=1, max_length=100)
    age: int = Field(..., gt=0, lt=20)


@validate_call
def greet(person: Person, message: Annotated[str, Field(min_length=1, max_length=100)]):
    print(f"Hello, {person.name}! {message}")


# 错误的调用,将引发验证错误
try:
    greet(Person(name="公众号:海哥python", age=18), 1)
except Exception as e:
    print(e)

此时,执行会报错:

1 validation error for greet
1
  Input should be a valid string [type=string_type, input_value=1, input_type=int]
    For further information visit https://errors.pydantic.dev/2.5/v/string_type

计算属性

字段可能派生自其他字段,比如年龄一般会根据生日和当前日期动态计算得出、面积通过长和宽动态计算等。

以下我们动态增加link字段为例:

#! -*-conding: UTF-8 -*-
# @公众号: 海哥python
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel, ValidationError, EmailStr, computed_field


class User(BaseModel):
    id: int
    name: str = "小卤蛋"
    age: int
    email: EmailStr
    signup_ts: Optional[datetime] = None
    friends: List[str] = []

    @computed_field  # 计算属性
    @property
    def link(self) -> str:
        return f"尼古拉斯 · {self.name}"


if __name__ == '__main__':

    user_data = {
        "id": 123,
        "name": "小卤蛋",
        "age": 20,
        "email": "xiaoludan@example.com",
        'signup_ts': '2024-07-19 00:22',
        'friends': ["公众号:海哥python", '小天才', b''],
    }
    #
    try:
        user = User(**user_data)
        print(f"{user.model_dump()} .... type: {type(user.model_dump())}")
    except ValidationError as e:
        print(f"Validation error: {e.json()}")

输出结果为(序列化后会发现多了link字段):

{'id': 123, 'name': '小卤蛋', 'age': 20, 'email': 'xiaoludan@example.com', 'signup_ts': datetime.datetime(2024, 7, 19, 0, 22), 'friends': ['公众号:海哥python', '小天才', ''], 'link': '尼古拉斯 · 小卤蛋'} .... type: <class 'dict'>

管理配置

 pip install pydantic_settings 

使用Pydantic的BaseSettings可以很方便的管理应用程序的配置。

# ! -*-conding: UTF-8 -*-
# @公众号: 海哥python
import os

# 从pydantic模块导入HttpUrl和Field类,用于设置和验证配置数据的类型和约束
from pydantic import HttpUrl, Field
# 从pydantic_settings模块导入BaseSettings类,作为配置类的基类
from pydantic_settings import BaseSettings

# 初始化环境变量,这些环境变量将用于配置应用程序的数据库和API访问
os.environ['DATABASE_HOST'] = "http://baidu.com"
os.environ['DATABASE_USER'] = "公众号:海哥python"
os.environ['DATABASE_PASSWORD'] = "123456abcd"
os.environ['API_KEY'] = "DHKSDsdh*(sdds"


class AppConfig(BaseSettings):
    """
    应用程序配置类,继承自BaseSettings,用于管理应用程序的配置信息。

    Attributes:
        database_host: 数据库主机的URL,必须是一个有效的HTTP或HTTPS URL。
        database_user: 数据库用户的名称,最小长度为5个字符。
        database_password: 数据库用户的密码,最小长度为10个字符。
        api_key: API访问的密钥,最小长度为8个字符。
    """
    # 定义配置项database_host,类型为HttpUrl,确保其为有效的HTTP或HTTPS URL
    database_host: HttpUrl
    # 定义配置项database_user,类型为字符串,默认最小长度为5
    database_user: str = Field(min_length=5)
    # 定义配置项database_password,类型为字符串,默认最小长度为10
    database_password: str = Field(min_length=10)
    # 定义配置项api_key,类型为字符串,默认最小长度为8
    api_key: str = Field(min_length=8)


# 打印配置类的实例化对象的模型信息,用于调试和确认配置的正确性
print(AppConfig().model_dump())

执行结果:

{'database_host': Url('http://baidu.com/'), 'database_user': '公众号:海哥python', 'database_password': '123456abcd', 'api_key': 'DHKSDsdh*(sdds'}

如果是配置文件等,则可以通过model_config进行配置。

新建一个.env配置文件:

DATABASE_HOST=http://baidu.com
DATABASE_USER=公众号:海哥python
DATABASE_PASSWORD=123456abcd
API_KEY=DHKSDsdh*(sdds
# ! -*-conding: UTF-8 -*-
# @公众号: 海哥python
# 导入Pydantic的HttpUrl和Field类,用于配置验证
from pydantic import HttpUrl, Field
# 导入BaseSettings和SettingsConfigDict类,用于设置配置类的基础行为和配置字典
from pydantic_settings import BaseSettings, SettingsConfigDict


class AppConfig(BaseSettings):
    """
    应用配置类,继承自BaseSettings,用于定义和管理应用的配置项。

    Attributes:
        model_config: 配置模型的设置,用于指定.env文件的位置、编码方式、是否大小写敏感以及额外的配置策略。
        database_host: 数据库主机的URL,必须是一个有效的HTTP或HTTPS URL。
        database_user: 数据库用户的名称,最小长度为5个字符。
        database_password: 数据库用户的密码,最小长度为10个字符。
        api_key: API的密钥,最小长度为8个字符。
    """
    # 定义配置模型的设置,包括.env文件位置、编码、大小写敏感性和额外参数策略
    model_config = SettingsConfigDict(
        env_file=".env",
        env_file_encoding="utf-8",
        case_sensitive=False,
        extra="forbid",
    )

    # 数据库主机的URL,必须是一个有效的HTTP或HTTPS URL
    database_host: HttpUrl
    # 数据库用户的名称,最小长度为5个字符
    database_user: str = Field(min_length=5)
    # 数据库用户的密码,最小长度为10个字符
    database_password: str = Field(min_length=10)
    # API的密钥,最小长度为8个字符
    api_key: str = Field(min_length=8)


# 打印配置类的实例化对象的模型信息,用于调试和确认配置的正确性
print(AppConfig().model_dump())

执行结果:

{'database_host': Url('http://baidu.com/'), 'database_user': '公众号:海哥python', 'database_password': '123456abcd', 'api_key': 'DHKSDsdh*(sdds'}

嵌套数据模型

Pydantic 支持嵌套的数据模型,方便管理复杂的数据结构。以下是一个示例代码:

#! -*-conding: UTF-8 -*-
# @公众号: 海哥python


from typing import List
from pydantic import BaseModel, conint


class Friend(BaseModel):
    name: str
    age: conint(gt=0, le=99)


class User(BaseModel):
    name: str
    age: conint(gt=0, le=99)
    friends: List[Friend]


# 创建并验证数据
user_data = {
    'name': '公众号:海哥python',
    'age': 30,
    'friends': [{'name': '小卤蛋', 'age': 3}, {'name': '李元芳', 'age': 18}]
}
user = User(**user_data)
print(user)  # name='公众号:海哥python' age=30 friends=[Friend(name='小卤蛋', age=3), Friend(name='李元芳', age=18)]

Field 对象

Pydantic 的 Field 函数是一个强大的工具,它允许你在模型字段上设置额外的验证规则和默认值。Field 函数通常与模型字段一起使用,以提供更多的定制选项。

以下是一些常用的参数:

参数具体含义
...表示该字段是必填项
default用于定义字段的默认值
default_factory用于定义字段的默认值函数
alias字段定义别名
validation_alias字段定义别名,只想将别名用于验证
serialization_alias 字段定义别名,只想定义用于序列化的别名
gt、lt、ge等约束数值,大于、小于、大于或等于等
min_length、max_length等约束字符串
min_items、max_items等元组、列表或集合约束
validate_default 控制是否应验证字段的默认值,默认情况下,不验证字段的默认值。
strict指定是否应在“严格模式”下验证字段
frozen用于模拟冻结的数据类行为
exclude用于控制导出模型时应从模型中排除哪些字段
pattern对于字符串字段,您可以设置为 pattern 正则表达式以匹配该字段所需的任何模式。

#! -*-conding: UTF-8 -*-
# @公众号: 海哥python

from pydantic import BaseModel, Field, EmailStr, ValidationError, SecretStr
from typing import List, Optional
from datetime import datetime


class User(BaseModel):
    id: int = Field(..., alias="_id", frozen=True, strict=True)  # 设置别名,创建后id不能被修改,id不能是字符串形式的“123”传入
    name: str = Field(default="小卤蛋", min_length=1, max_length=100)  # 设置默认值,使用 min_length 和 max_length 来限制字符串长度
    age: int = Field(gt=0)  # 支持各类条件验证,这里假设年龄必须大于0
    email: EmailStr
    signup_ts: Optional[datetime] = Field(default_factory=datetime.now, nullable=False, validate_default=True)
    friends: List[str] = Field(default=[], min_items=0)
    passwd: SecretStr = Field(min_length=6, max_length=20, exclude=True)  # passwd不会被序列化


if __name__ == '__main__':

    print(User.model_json_schema())

    user_data = {
        "_id": 123,  # 使用别名 _id
        "name": "小卤蛋",
        "age": 20,
        "email": "xiaoludan@example.com",
        # 'signup_ts': '2024-07-19 00:22',
        'friends': ["公众号:海哥python", '小天才', b''],
        "passwd": "123456"
    }

    try:
        user = User(**user_data)
        print(f"创建用户: {user}")
        print(f"转成字典形式: {user.model_dump()} .... type: {type(user.model_dump())}")
        print(f"转成json格式:{user.model_dump_json()} .... type: {type(user.model_dump_json())}")
        print(f"用户属性: User id: {user.id}, User name: {user.name}, User email: {user.email}")
        # user.id = 456   # 这里修改会报错
    except ValidationError as e:
        print(f"Validation error: {e.json()}")

将得到结果:

{'properties': {'_id': {'title': ' Id', 'type': 'integer'}, 'name': {'default': '小卤蛋', 'maxLength': 100, 'minLength': 1, 'title': 'Name', 'type': 'string'}, 'age': {'exclusiveMinimum': 0, 'title': 'Age', 'type': 'integer'}, 'email': {'format': 'email', 'title': 'Email', 'type': 'string'}, 'signup_ts': {'anyOf': [{'format': 'date-time', 'type': 'string'}, {'type': 'null'}], 'nullable': False, 'title': 'Signup Ts'}, 'friends': {'default': [], 'items': {'type': 'string'}, 'minItems': 0, 'title': 'Friends', 'type': 'array'}, 'passwd': {'maxLength': 20, 'minLength': 6, 'title': 'Passwd', 'type': 'string'}}, 'required': ['_id', 'age', 'email', 'passwd'], 'title': 'User', 'type': 'object'}
创建用户: id=123 name='小卤蛋' age=20 email='xiaoludan@example.com' signup_ts=datetime.datetime(2024, 7, 23, 11, 22, 46, 137194) friends=['公众号:海哥python', '小天才', ''] passwd='123456'
转成字典形式: {'id': 123, 'name': '小卤蛋', 'age': 20, 'email': 'xiaoludan@example.com', 'signup_ts': datetime.datetime(2024, 7, 23, 11, 22, 46, 137194), 'friends': ['公众号:海哥python', '小天才', '']} .... type: <class 'dict'>
转成json格式:{"id":123,"name":"小卤蛋","age":20,"email":"xiaoludan@example.com","signup_ts":"2024-07-23T11:22:46.137194","friends":["公众号:海哥python","小天才",""]} .... type: <class 'str'>
用户属性: User id: 123, User name: 小卤蛋, User email: xiaoludan@example.com

Config 配置选项

如果要对BaseModel中的某一基本型进行统一的格式要求,我们还可以使用Config类来实现。

以下是一些 Config 类中常见的属性及其含义:

参数取值类型具体含义
str_min_lengthintstr 类型的最小长度,默认值为None
str_max_lengthintstr 类型的最大长度。默认值为None
extrastr在模型初始化期间是否忽略、允许或禁止额外的属性。默认值为 ‘ignore’。allow - 允许任何额外的属性。forbid - 禁止任何额外的属性。ignore - 忽略任何额外的属性。
frozen bool模型是否可变
str_to_upper bool是否将 str 类型的所有字符转换为大写。默认值为 False 。
str_strip_whitespacebool是否去除 str 类型的前导和尾随空格。
str_to_lowerbool是否将 str 类型的所有字符转换为小写。默认值为 False 。

#! -*-conding: UTF-8 -*-
# @公众号: 海哥python

from pydantic import BaseModel


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

    class Config:
        str_min_length = 10  # 字符串最小长度
        str_max_length = 20  # 字符串最大长度


user = User(name="John Doe", age=30)

执行将得到结果:

Validation error: [{"type":"string_too_short","loc":["name"],"msg":"String should have at least 10 characters","input":"John Doe","ctx":{"min_length":10},"url":"https://errors.pydantic.dev/2.5/v/string_too_short"}]

有关Config类中的特殊关键词名称,这里只给出了两个简单的例子,更多的内容可以参考官网中的文档说明。

序列化

使用模型类.model_dump()方法可以将一个模型类实例对象转换为字典类型数据。

#! -*-conding: UTF-8 -*-
# @公众号: 海哥python
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel, ValidationError, EmailStr, field_validator, field_serializer
from enum import Enum


class GenderEnum(str, Enum):
    """
    性别枚举
    """
    male = "男"
    female = "女"


class User(BaseModel):
    id: int
    name: str = "小卤蛋"
    age: int
    email: EmailStr
    signup_ts: Optional[datetime] = datetime.now()
    friends: List[str] = []
    sex: GenderEnum

    @field_validator("age")
    @classmethod
    def check_age(cls, age):
        if age < 18:
            raise ValueError("用户年龄必须大于18岁")
        return age

    @field_serializer('signup_ts', when_used="always")
    def serialize_signup_ts(self, value: datetime) -> str:
        return value.strftime('%Y-%m-%d %H:%M:%S')

    @field_serializer('sex', when_used="always")
    def serialize_sex(self, value) -> str:
        return value.value


if __name__ == '__main__':

    user_data = {
        "id": 123,
        "name": "小卤蛋",
        "age": 20,
        "email": "xiaoludan@example.com",
        # 'signup_ts': '2024-07-19 00:22',
        'friends': ["公众号:海哥python", '小天才', b''],
        "sex": "男",
    }

    try:
        user = User.model_validate(user_data)
        print(f"{user.model_dump()} .... type: {type(user.model_dump())}")
    except ValidationError as e:
        print(f"Validation error: {e.json()}")

默认情况下, datetime 对象被序列化为 ISO 8601 字符串。这里使用field_serializer自定义序列化规则。

{'id': 123, 'name': '小卤蛋', 'age': 20, 'email': 'xiaoludan@example.com', 'signup_ts': '2024-07-24 14:47:33', 'friends': ['公众号:海哥python', '小天才', ''], 'sex': '男'} .... type: <class 'dict'>

使用模型类.model_dump_json()方法可以将一个模型类实例对象转换为JSON字符串。

#! -*-conding: UTF-8 -*-
# @公众号: 海哥python
from datetime import datetime
from typing import List, Optional, Any
from pydantic import BaseModel, ValidationError, EmailStr, field_validator, field_serializer, model_serializer


class User(BaseModel):
    id: int
    name: str = "小卤蛋"
    age: int
    email: EmailStr
    signup_ts: Optional[datetime] = datetime.now()
    friends: List[str] = []

    @field_validator("age")
    @classmethod
    def check_age(cls, age):
        if age < 18:
            raise ValueError("用户年龄必须大于18岁")
        return age

    @field_serializer('signup_ts', when_used="json")
    def serialize_signup_ts(self, value: datetime) -> str:
        return value.strftime('%Y-%m-%d %H:%M:%S')

    @model_serializer(when_used="json")
    def serialize_model(self) -> dict[str, Any]:
        return {
            'id': self.id,
            'name': self.name,
            'age': self.age + 1,
            'email': self.email,
            'signup_ts': self.serialize_signup_ts(self.signup_ts),
            'friends': self.friends,
        }


if __name__ == '__main__':

    user_data = {
        "id": 123,
        "name": "小卤蛋",
        "age": 20,
        "email": "xiaoludan@example.com",
        # 'signup_ts': '2024-07-19 00:22',
        'friends': ["公众号:海哥python", '小天才', b''],
    }

    try:
        user = User.model_validate(user_data)
        print(f"{user.model_dump_json()} .... type: {type(user.model_dump_json())}")
        print(f"{user.model_dump()} .... type: {type(user.model_dump())}")
    except ValidationError as e:
        print(f"Validation error: {e.json()}")

也可以使用model_serializer对整体模型的序列化做定制。结果如下:

{"id":123,"name":"小卤蛋","age":21,"email":"xiaoludan@example.com","signup_ts":"2024-07-24 14:17:42","friends":["公众号:海哥python","小天才",""]} .... type: <class 'str'>
{'id': 123, 'name': '小卤蛋', 'age': 20, 'email': 'xiaoludan@example.com', 'signup_ts': datetime.datetime(2024, 7, 24, 14, 17, 42, 45474), 'friends': ['公众号:海哥python', '小天才', '']} .... type: <class 'dict'>

生成文档

Pydantic 可以自动生成 API 文档。

#! -*-conding: UTF-8 -*-
# @公众号: 海哥python
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel, EmailStr, field_validator


class User(BaseModel):
    id: int
    name: str = "小卤蛋"
    age: int
    email: EmailStr
    signup_ts: Optional[datetime] = None
    friends: List[str] = []

    @field_validator("age")
    def check_age(cls, age):
        if age < 18:
            raise ValueError("用户年龄必须大于18岁")
        return age


if __name__ == '__main__':
    print(User.model_json_schema())

通过model_json_schema方法可以得到API文档。

{'properties': {'id': {'title': 'Id', 'type': 'integer'}, 'name': {'default': '小卤蛋', 'title': 'Name', 'type': 'string'}, 'age': {'title': 'Age', 'type': 'integer'}, 'email': {'format': 'email', 'title': 'Email', 'type': 'string'}, 'signup_ts': {'anyOf': [{'format': 'date-time', 'type': 'string'}, {'type': 'null'}], 'default': None, 'title': 'Signup Ts'}, 'friends': {'default': [], 'items': {'type': 'string'}, 'title': 'Friends', 'type': 'array'}}, 'required': ['id', 'age', 'email'], 'title': 'User', 'type': 'object'}

应用场景

Flask集成Pydantic

以 Flask 为例,我们亦可以很方便的集成Pydantic:

#! -*-conding: UTF-8 -*-
# @公众号: 海哥python
from functools import wraps

from flask import Flask, request, jsonify
from pydantic import BaseModel, ValidationError

app = Flask(__name__)


# 创建一个 Pydantic 模型来表示请求体中的数据
class User(BaseModel):
    model_config = {"extra": "forbid"}  # 不允许额外字段
    id: int
    name: str
    email: str


def validate_request_body(model):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            try:
                body = model.model_validate(request.json)
                return func(body, *args, **kwargs)
            except ValidationError as e:
                return jsonify({"error": e.errors()}), 400

        return wrapper

    return decorator


@app.route('/users', methods=['POST'])
@validate_request_body(User)
def create_user2(user: User):
    # 在这里可以处理用户数据,例如保存到数据库
    # ...
    print(user.model_dump())

    # 返回成功响应
    return jsonify({"message": "User created successfully", "user": user.dict()}), 201


if __name__ == '__main__':
    app.run(debug=True)

参数验证失败时:

curl -X POST      -H "Content-Type: application/json"      -d '{"id": 1, "name": 1, "email": "john.doe@example.com"}'      http://localhost:5000/users
{
  "error": [
    {
      "input": 1,
      "loc": [
        "name"
      ],
      "msg": "Input should be a valid string",
      "type": "string_type",
      "url": "https://errors.pydantic.dev/2.8/v/string_type"
    }
  ]
}

所有验证错误,包括 ValueError 等我们在自定义验证器中提出的错误,都包含在 Pydantic 的 ValidationError。因此,一种常见的做法是为其设置全局错误处理程序。

flask_pydantic也为您提供一种集成Falsk和Pydantic的现成方案,感兴趣的话可以自行研究。


LangChain中使用Pydantic

以下,我们借助LangChain和Pydantic实现一个基于通义千问模型的问答路由系统。

新建.env文件:

DASHSCOPE_API_KEY=sk-b920635xxxdsdsdsd7edc2590sds20300  # 换成自己的

根据用户的问题类型,选择向量存储或网络搜索来提供答案,并将答案解析为结构化的数据:

# 导入环境变量加载工具和必要的库
import os
from dotenv import find_dotenv, load_dotenv
from typing import Literal

# 导入通义千问模型相关的类和模块
from langchain_community.chat_models import QianfanChatEndpoint
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_openai import ChatOpenAI

# 加载环境变量
load_dotenv(find_dotenv())
# 从环境变量中获取DashScope的API密钥
DASHSCOPE_API_KEY = os.environ["DASHSCOPE_API_KEY"]


# 定义一个模型,用于路由用户查询到合适的数据库
# 定义 数据格式
class RouteQuery(BaseModel):
    """用于路由用户查询的模型,决定查询应路由到vectorstore还是web_search。

    Attributes:
        datasource (Literal["vectorstore", "web_search"]): 查询应路由到的数据源,可以是"vectorstore"或"web_search"。
    """
    datasource: Literal["vectorstore", "web_search"] = Field(
        ...,
        description="根据用户问题选择将其路由到网络搜索还是vectorstore。",
    )


if __name__ == '__main__':
    # 初始化通义千问模型
    api_key = DASHSCOPE_API_KEY
    qwen_model = ChatOpenAI(
        model_name="qwen-max",
        openai_api_base="https://dashscope.aliyuncs.com/compatible-mode/v1",
        openai_api_key=api_key,
    )
    llm = qwen_model

    # 初始化输出解析器,用于解析模型的输出
    parser = PydanticOutputParser(pydantic_object=RouteQuery)

    # 获取数据格式说明
    data_pattern = parser.get_format_instructions()

    # 定义路由提示模板
    # Prompt
    system = """你是一个用户查询路由专家,负责将用户查询路由到vectorstore或web搜索。
    vectorstore包含有关代理、prompt工程和对抗攻击的文档。
    对于这些主题的问题,使用vectorstore。否则,使用web搜索。
    生成格式化数据模式如下:
    {data_pattern}  
    """
    route_prompt = ChatPromptTemplate.from_messages(
        [
            ("system", system),
            ("human", "{question}"),
        ]
    )

    # 构建查询路由管道
    question_router = route_prompt | llm

    # 调用查询路由管道并获取结果
    # 示例问题:公众号:海哥python 有多少粉丝?
    res = question_router.invoke({"question": "公众号:海哥python 有多少粉丝?", "data_pattern": data_pattern})
    # res = question_router.invoke({"question": "如何向代理添加记忆?", "data_pattern": data_pattern})

    # 解析获取的结果
    content = res.content
    print(content)

这样,我们就得到一个结构化的数据:

{"datasource": "web_search"}

后续便可以根据这个统一的结构化数据选择适合的源进行检索。

小结

目前,Python 中主流的数据验证库还有不少,但是 Pydantic 无疑是那个星星增长最迅猛的。

总而言之,Pydantic 是一个灵活而强大的数据验证库,它利用 Python 类型注解来简化数据验证和解析。无论是处理用户输入、验证数据库查询结果,还是解析 API 响应,都能提供直观而高效的解决方案。

Pydantic 的强大远不止这些,更多使用技巧请查阅官方文档 …

最后

你是否也在项目中使用过Pydantic吗?您以什么形式使用呢?对于Pyddantic您有更多的心得吗?欢迎在评论区告诉我。

今天的分享就到这里。如果觉得不错,点赞,在看,关注安排起来吧。

参考

https://mp.weixin.qq.com/s/uB8ff3sJjz2fx1n5cmckaw
https://mp.weixin.qq.com/s/wAUxaZOjPIaaeLWCH9Tk7g
https://realpython.com/python-pydantic/
https://shzhangji.com/blog/2024/01/28/define-api-data-models-with-pydantic/
https://www.netguru.com/blog/data-validation-pydantic
https://jsontopydantic.com/

FastAPI 开发中数据校验利器 Pydantic 介绍与集成使用 前言当我们在处理一些数据来自系统外部的时候,可能来自 API、终端用户输入或者其他途径,Web 开发中有句话叫做:永远不要相信用户的输入。所以,我们可能需要检查、校验这些数据,将这些数据格式化、标准化,以至于这些数据满足我们真实程序的数据输入,保证项目的正确执行。Pydantic 介绍Pydantic 是一个 Python... 阅读详情

相关推荐

【Python】 Pydantic 库

Pydantic 是一个基于 Python 类型提示 (Type Hints) 进行数据验证、序列化和反序列化、以及设置管理的库。它利用 Python 3.6+ 引入的类型注解,使得开发者能够以声明式的方式定义数据模型,并自动获得强大的数据校验能力和清晰的数据结构。简单来说,你通过编写 Python 类来定义你的数据应该是什么样子(字段、类型、约束等),Pydantic 则负责确保任何输入数据都符合这些定义。如果数据有效,Pydantic 会创建一个包含该数据的模型实例;如果数据无效,它会返回一个易于理解的

宅男很神经 2815

python库pydantic简易教程

pydantic 库是 python 中用于数据接口定义检查与设置管理的库。pydantic 在运行时强制执行类型提示,并在数据无效时提供友好的错误。1.与 IDE/linter 完美搭配,不需要学习新的模式,只是使用类型注解定义类的实例2.多用途,BaseSettings 既可以验证请求数据,也可以从环境变量中读取系统设置快速3.可以验证复杂结构4.可扩展,可以使用validator装饰器装饰的模型上的方法来扩展验证。

weixin_43936332的博客 9454

python中pydantic库

不知道大家是否非常羡慕C语言等在进行函数传参时,可以指定数据类型来传参呢?我之前有一篇讲过使用typing来指定数据类型,但是其仅仅是能指定数据类型,只能做一个提醒的作用,那么我们如何来结合typing模块,来写一个可以像Java等语言的指定参数类型呢?这里我推荐pydantic库。首先,在学这个库之前,我们需要去回顾一下typing然后,我们来解释一下我们即将要学的库:其使用 Python 类型注释的数据验证和设置管理。pydantic在运行时强制执行类型提示,并在数据无效时提供用户友好的错误。

一个专注于机器学习基础与实战的技术博客,内容涵盖算法推导、模型实现、数学原理与代码实践。用通俗的语言解析复杂概念,记录学习过程中的思考与总结,适合机器学习爱好者和从业者参考。 3133

pydantic学习与使用-1.pydantic简介与基础入门

前言 版本文档:v1.9.0 使用 python 类型注释的数据验证和设置管理。 pydantic在运行时强制执行类型提示,并在数据无效时提供用户友好的错误。 定义数据应该如何在纯粹的、规范的 python 中;并使用 pydantic 对其进行验证。 pydantic 简介 pydantic 主要是一个解析库,而不是验证库。 验证是达到目的的一种手段:建立一个符合所提供的类型和约束的模型。 换句话说,pydantic 保证输出模型的类型和约束,而不是输入数据。 这听起来像是一个深奥的区别,但事实并非如此。

qq_27371025的博客 8655

python-51-使用最广泛的数据验证库Pydantic

Pydantic是Python使用最广泛的数据验证库,基于Python类型提示来实现数据的校验和设定限制条件。

qq_20466211的博客 462

国密USB Key开发实战:SKF库调用全流程解析(附代码示例)

本文详细解析了国密USB Key开发中SKF库的调用全流程,涵盖从设备枚举、应用容器遍历到数字签名与验证的核心步骤。通过实战代码示例,深入探讨了SKF库的层次化模型、SM2签名实现细节以及纯软件验签方案,为开发电子签章、安全登录等合规应用提供了一套完整的调用开发指南。

oo7890的博客 524

【Pydantic】Python 数据验证入门

Pydantic 是一个功能强大的 Python 数据验证库,它通过 Python 类型注解实现快速的数据验证和转换。它不仅提供了全面的类型验证、自动数据转换和详细的错误处理机制,还具有基于 Rust 实现的高性能核心验证器。凭借其优秀的 IDE 支持和可扩展性,Pydantic 在 FastAPI 等 Web 框架、配置管理、数据序列化以及 API 接口模型定义等多个场景中得到广泛应用。

Creedassin的博客 4499

手写数字数据集MNIST

文章目录一、 简介二、下载MNIST数据集训练集和测试集的长度输出图像数据和标记数据的形状显示手写数字图片输出数据集中的第1个样本显示图片 一、 简介 MNIST是一个非常有名的手写体数字识别数据集(手写数字灰度图像数据集),在很多资料中,这个数据集都会被用作深度学习的入门样例。 MNIST数据集是由0到9的数字图像构成的。训练图像有6万张,测试图像有1万张。MNIST数据集是NIST数据集的一个子集,它包含了60000张图片作为训练数据,10000张图片作为测试数据。由250个不同的人手写而成。 每一张图

qq_37289115的博客 1万+

python常用库之数据验证库pydantic

Pydantic 会检查传入的数据类型是否符合定义的类型。例如,如果某个属性被定义为 int,而用户传入了一个字符串,Pydantic 会抛出一个错误,确保数据的正确性。

西京刀客 2260

超详细 Git创建仓库及上传项目至Github操作(Windows系统)

文章目录前言一、Git下载及安装二、创建代码仓库1.配置身份2.创建本地仓库3.提交本地代码三、使用Git上传代码至Github1.创建ssh keys2.在Github中新建仓库3.关联本地仓库与Github仓库4.推送代码至Github仓库总结 前言 本文对于Windows系统上git的安装及基本使用方法进行简单介绍,并介绍如何使用git将仓库中的项目上传至个人的Github中去。内容参考《第一行代码》以及一些博客。 本文适合新手阅读哦~ 一、Git下载及安装 打开网站 git下载 下载安装包(直接

lijianyi0219的博客 2万+

Trae CN 使用入门指南

【代码】Trae CN 使用入门指南。

weixin_42551921的博客 8761

Pydantic 数据验证

Pydantic数据验证

Mark_Australia的博客 1112

Word多级标题出现黑块的解决思路(word 数字必须介于1-1638之间)

遇到多级目录出现黑块问题,看到一篇博客完美解决,在此记录。

AI_Optimization的博客 3714

Pydantic:Python 数据验证库的新宠

Pydantic 是一个在 Python 中用于数据验证和解析的第三方库。它提供了一种简单且直观的方式来定义数据模型,并使用这些模型对数据进行验证和转换。Pydantic 使用前需要先进行安装。使用 Pydantic,可以定义一个模型类,该类需要继承pydantic中的BaseModel类,模型类描述了数据的结构和类型,并指定验证规则。然后,可以使用这个模型类来验证输入的数据是否符合预期,并以类型安全的方式访问和操作数据。如果创建实例的数据不符合类型注解的要求,则会报错误。Pydantic 还可以结合。

m0_58477260的博客 1070

UG\NX二次开发 找不到指定的dlx文件(Specified Dlx file not found)

有群友问:用Ctrl+U运行的时候就报错,找不到指定的dlx文件Specified Dlx file not found。怎么处理。

王牌飞行员_里海的博客 1127

2、Elixir与Erlang:构建高可用系统的利器

本文介绍了Elixir与Erlang在构建高可用系统方面的优势,深入探讨了Erlang的并发机制、容错能力以及分布式特性,并结合实际应用场景展示了其在服务器端开发中的强大能力。此外,还提供了学习资源和交流平台,帮助开发者快速上手并深入实践。

grape的博客 124

Python——pydantic库

Pydantic 是一个用于 Python 的数据验证库,特别适用于构建 RESTful API 服务,尤其是结合 FastAPI 和 Starlette 框架时。它利用 Python 的类型注解系统,可以高效且准确地验证和清理传入应用程序的数据。Pydantic 提供了强类型模式验证,能够确保数据结构一致性和安全性。

pumpkin84514的博客 1369

《C++等级认证CCF-GESP真题解析》专栏总目录

❤️ 专栏名称:《C++等级认证CCF-GESP真题解析》🌸 专栏介绍:中国计算机学会GESP《CCF编程能力等级认证》C++编程(1~8级)历届真题解析。🚀 订阅专栏:订阅后可阅读专栏内所有真题解析,真题持续更新中,欢迎订阅!

人生就是一个不断学习的过程 6287

Pydantic Python 数据验证和设置管理库

Python复制id: intname: stremail: stris_active: bool = False # 默认值# 创建一个 User 实例# 输出:Pydantic 是一个强大的工具,用于数据验证和设置管理。通过定义模型和字段,Pydantic 可以自动验证数据的类型和格式,并提供序列化功能。它广泛用于 FastAPI 等框架中,用于处理请求数据的验证和响应数据的序列化。

codau的博客 998

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Pydantic:Python 数据验证与处理的强大工具

Pydantic 允许开发人员定义自定义的验证规则,这使得在需要的时候可以实现复杂的验证逻辑。开发人员可以使用validator装饰器来定义自定义的验证函数,对特定字段进行更加复杂的验证。例如,可以定义一个验证函数来检查电子邮件地址的格式是否正确,或者检查密码的强度是否符合要求。name: strage: int在这个例子中,我们定义了一个名为User的模型,包含name和age两个必填字段,以及一个可选的email字段,默认值为None。

rock---you的博客 2022

【Python】Pydantic 库:数据验证和序列化

Pydantic 是一个 Python 库,用于数据验证和序列化,基于 Python 的类型注解。它通过定义数据模型(类)来验证输入数据、转换类型、提供默认值,并支持 JSON 序列化与反序列化。Pydantic 以其简单易用的接口、高性能和强大的类型检查功能,广泛应用于 API 开发、配置文件管理和数据处理。

彬彬侠的博客 1423

pydantic数据验证库

pydantic文档https://pydantic-docs.helpmanual.io/ 使用 python 类型注释的数据验证和设置管理。pydantic在运行时强制执行类型提示,并在数据无效时提供用户友好的错误。定义数据应该如何在纯粹的、规范的 python 中;用pydantic验证它。 例子 from datetime import datetime from typing import List, Optional from pydantic import BaseModel .

Ch3nnn的博客 1326

数据验证新技术:Python详细解析Pydantic

数据验证和处理是编程中的常见任务之一。在 Python 中,有许多库可用于数据验证和处理,其中一个流行的选择是 Pydantic。Pydantic 是一个强大的库,用于定义数据模型、验证输入数据、进行数据转换和生成文档。本文将介绍 Pydantic 的基本概念、用法和示例,帮助大家更好地理解如何使用它来管理数据。

Rocky006的博客 2386
上一篇: python函数参数定义中的这两个分隔符,还有人不知道吗?
下一篇: 强烈推荐这个大佬的 Redis 系列,看完直呼过瘾(文末赠书)
海哥python
博客等级 码龄9年 1487粉丝 74原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海哥python

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值