一、引言
以下Pydantic使用的版本为2.0.0,不同版本的Pydantic可能会有一些差异,特别是在一些新的功能和废弃的功能上,建议查看官方文档。
1.1 什么是Pydantic Pydantic是Python中最广泛使用的基于Python类型提示的数据验证和设置管理库,它遵循纯正的Python 3.9+标准,能帮助开发者轻松验证数据、管理配置并减少样板代码。该库快速且可扩展,能与代码分析器、集成开发环境良好配合,便于进行数据验证工作。
1.2 为什么选择Pydantic
由类型提示驱动 :用Pydantic的时候,模式验证和序列化都靠类型注解来控制。就不用学太多东西,也不用写很多代码,还能和IDE以及静态分析工具完美配合。
速度快 :Pydantic的核心验证逻辑是用Rust写的,所以它是Python里最快的数据验证库之一。
支持JSON Schema :Pydantic模型能生成JSON Schema,这样就能轻松和其他工具集成。
严格模式和宽松模式 :Pydantic有两种模式可以选。严格模式下,数据不会被转换;宽松模式下,Pydantic会在合适的时候把数据转换成正确的类型。
支持多种标准库类型 :Pydantic能验证很多标准库的类型,像数据类、类型字典这些都不在话下。
可自定义 :Pydantic允许自定义验证器和序列化器,用各种方式来改变数据处理的方法。
生态系统强大 :PyPI上大概有8000个包都在用Pydantic,像FastAPI、huggingface、Django Ninja、SQLModel和LangChain这些流行的库也都在用。
经过实战检验 :Pydantic每个月的下载量超过3.6亿次,FAANG所有公司,还有纳斯达克25家最大企业里的20家都在用它。
以上内容是摘自官网的,不过也确实很好用,在项目中用的比较多😄,特别是用Fast API做后端的时候,会帮你规范接收的JSON数据,多了一个校验层,减少了很多的错误。
1.3 应用场景
API请求和响应验证 :这个也是我实际工作中用的比较多的,用Fast API作为后端接收数据时,会使用Pydantic来验证客户端发送的请求数据和服务器返回的响应数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from fastapi import FastAPIfrom pydantic import BaseModelapp = FastAPI() class Item (BaseModel ): name: str price: float is_offer: bool = None @app.post("/items/" ) async def create_item (item: Item ): return {"item_name" : item.name, "item_price" : item.price, "is_offer" : item.is_offer}
这个示例中的流程如下:
导入FastAPI和BaseModel
创建FastAPI应用实例
定义与之前相同的Item模型
创建POST路由,使用Item模型验证请求体
在路由处理函数中,直接使用验证后的item数据
当客户端发送POST请求到/items/
端点时,FastAPI获取的数据会先通过Pydantic模型进行验证,如果不符合会返回详细的错误信息,这里的错误信息是Pydantic提供的,不是FastAPI提供的,Fast API作为后端获取客户端发来的数据。这也侧面说明Pydantic可以在很多场景下使用,不仅限于Fast API。
配置管理 :管理应用程序的配置,确保配置数据的正确性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 from pydantic import BaseModel, Fieldfrom typing import Optional from pydantic_settings import SettingsConfigDictclass Settings (BaseModel ): app_name: str = "Awesome API" app_version: str = "1.0.0" debug: bool = False admin_email: str admin_phone: Optional [str ] = None db_host: str = "localhost" db_port: int = 5432 db_username: str = "postgres" db_password: str = Field(..., description="数据库密码,不应硬编码" ) db_name: str = "myapp" items_per_user: int = 50 api_key: str = Field(..., description="API密钥,不应硬编码" ) jwt_secret: str = Field(..., description="JWT密钥,不应硬编码" ) jwt_expiry_minutes: int = 30 model_config = SettingsConfigDict( env_file=".env" , case_sensitive=True , env_prefix="APP_" , extra="forbid" ) try : settings = Settings() print (f"应用名称: {settings.app_name} " ) print (f"数据库连接: postgresql://{settings.db_username} :*****@{settings.db_host} :{settings.db_port} /{settings.db_name} " ) except Exception as e: print (f"配置加载失败: {e} " ) """ 配置加载失败: 4 validation errors for Settings admin_email Field required [type=missing, input_value={}, input_type=dict] For further information visit https://errors.pydantic.dev/2.11/v/missing db_password Field required [type=missing, input_value={}, input_type=dict] For further information visit https://errors.pydantic.dev/2.11/v/missing api_key Field required [type=missing, input_value={}, input_type=dict] For further information visit https://errors.pydantic.dev/2.11/v/missing jwt_secret Field required [type=missing, input_value={}, input_type=dict] For further information visit https://errors.pydantic.dev/2.11/v/missing """
不难发现,Pydantic的配置管理功能非常强大,能够帮助我们更好的管理应用的配置,减少错误的发生。本质上还是做了数据的校验,只是校验的内容是配置项。配合其他库使用,会更加方便。
比如,你可以将这些配置与上面举例的FastAPI结合使用,例如:
1 2 3 4 5 6 7 from fastapi import FastAPIapp = FastAPI(title=settings.app_name, version=settings.app_version) @app.get("/" ) async def root (): return {"app_name" : settings.app_name, "version" : settings.app_version}
这样可以使你的应用配置更加规范和灵活,同时提高代码的可维护性和安全性。
在实际工程中可以单独开一个文件夹来存放用Pydantic
规范好的数据模型,更好做数据类型的统一管理。
数据序列化和反序列化 :将数据在不同格式之间转换,如JSON和Python对象之间的转换。Pydantic 提供了丰富的序列化功能,具体如下:
转换为 Python 字典(包含相关 Python 对象) :可将模型转换为由相关 Python 对象组成的 Python 字典。
转换为可 JSON 序列化的 Python 字典 :能把模型转换为仅由“可转换为 JSON 的”类型组成的 Python 字典。
转换为 JSON 字符串 :支持将模型直接转换为 JSON 字符串。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 from pydantic import BaseModelfrom datetime import datetimefrom typing import List class User (BaseModel ): id : int name: str signup_date: datetime tags: List [str ] user_data = '{"id": 1, "name": "John Doe", "signup_date": "2023-01-01T12:00:00", "tags": ["premium", "active"]}' user = User.model_validate_json(user_data) print ("反序列化结果:" )print (user)print (f"用户ID: {user.id } , 名称: {user.name} " )print (f"注册日期类型: {type (user.signup_date)} " )user_dict = user.model_dump() print ("\n转换为包含Python对象的字典:" )print (user_dict)print (f"字典中的日期类型: {type (user_dict['signup_date' ])} " )json_compatible_dict = user.model_dump(exclude_unset=True , by_alias=True ) print ("\n转换为可JSON序列化的字典:" )print (json_compatible_dict)user_json = user.model_dump_json() print ("\n转换为JSON字符串:" )print (user_json)class CustomUser (BaseModel ): id : int name: str signup_date: datetime class Config : json_encoders = { datetime: lambda v: v.strftime('%Y-%m-%d' ), } custom_user = CustomUser(id =2 , name="Jane Smith" , signup_date=datetime.now()) custom_json = custom_user.model_dump_json(indent=2 ) print ("\n自定义JSON编码器结果:" )print (custom_json)
这些功能使得Pydantic成为处理数据验证和序列化的强大工具,特别适合在API开发、配置管理和数据交换场景中使用。
序列化和反序列化的功能在实际工程中用的比较多,比如在Web应用中,客户端发送的请求数据是JSON格式的,服务器端需要将JSON格式的数据转换为Python对象进行处理,处理完成后再转换为JSON格式返回给客户端。
数值校验 :对数值类型的字段进行校验,确保输入的数值在指定的范围内。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from typing import Any from pydantic import BaseModel, field_validatorclass Model (BaseModel ): value: str @field_validator('value' , mode='before' ) @classmethod def cast_ints (cls, value: Any ) -> Any : if isinstance (value, int ): return "这是数字" else : return "这是字符串" print (Model(value='a' ))print (Model(value=1 ))
这个示例更贴近实际应用场景,特别是在用户注册、表单提交等需要严格数据验证的场景中非常有用。通过Pydantic,我们可以轻松实现复杂的验证逻辑,同时保持代码的清晰和可维护性。
特意给了个奇怪的示例,其实是想说,既然我可以用这个来做判断,就可以做转换🤭,如果我把”这是字符串”和”这是数字”转换成对应的异常信息返回、或者是类型转换代码,是不是可以做到统一输入了,即使收到的数据类型不同也不会影响后续的代码运行结果。
数据清洗和转换 :清理和转换输入数据,使其符合预期格式。
1 2 3 4 5 6 7 8 from pydantic import BaseModelclass Data (BaseModel ): value: int data = Data(value="42" ) print (data.value)
当然Pydantic本身也具备自动转换数据类型的能力,上面这个例子就是把字符串”42”转换为整数42。Pydantic很强大,有很多潜在的能力等待大家发掘,具体可以看看Pydantic官网,这里就不把全部特性都放出来了,篇幅很大。
二、安装与基础配置 2.1 安装Pydantic
Pydantic
也支持conda
安装,命令如下:
1 conda install pydantic -c conda-forge
Pydantic具有以下可选依赖项:
email:由emailValidator程序包提供的电子邮件验证。
timezone:由tzdata程序包提供的IANA时区数据库的备用选项。 要与Pydantic一起安装可选的依赖项:
1 2 3 4 pip install 'pydantic[email]' pip install 'pydantic[email,timezone]'
三、核心功能与优势 最后就把Pydantic的核心功能和优势总结一下,上面的应用场景其实都有涉及,大家看完下面的功能会有更清晰的认识。
3.1 数据验证 Pydantic 提供了强大而灵活的数据验证机制,确保输入数据符合预期格式和约束。
内置验证器 :提供了丰富的预定义验证器,如 EmailStr
(邮箱验证)、UrlStr
(URL验证)、constr
(字符串约束)、conint
(整数约束)等,覆盖了大多数常见验证场景。
自定义验证器 :通过 @validator
装饰器可以轻松创建自定义验证逻辑,支持字段级和模型级验证,还可以访问其他字段的值进行联合验证。
验证错误处理 :验证失败时会生成结构化的ValidationError
对象,包含详细的错误信息(字段、错误类型、错误消息),便于定位和处理问题。
示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 from pydantic import BaseModel, EmailStr, field_validator, ValidationErrorfrom pydantic_core.core_schema import FieldValidationInfoclass User (BaseModel ): email: EmailStr age: int password: str @field_validator('age' ) def age_must_be_adult (cls, v ): if v < 18 : raise ValueError('必须是成年人' ) return v @field_validator('password' ) def password_must_be_strong (cls, v ): if len (v) < 8 : raise ValueError('密码长度必须至少为8位' ) if not any (c.isupper() for c in v): raise ValueError('密码必须包含至少一个大写字母' ) return v try : User(email='invalid-email' , age=16 , password='weak' ) except ValidationError as e: print (e.json())
3.2 类型提示与自动转换 Pydantic 充分利用 Python 的类型提示系统,提供静态类型检查和运行时类型转换的双重保障。
支持的类型 :全面支持 Python 内置类型(int
, float
, str
, bool
等)、标准库类型(datetime
, date
, UUID
等)以及自定义类型。
自动类型转换 :智能地将输入数据转换为声明的类型,例如将字符串形式的数字转换为整数,将ISO格式的字符串转换为 datetime
对象。
复杂类型处理 :优雅处理嵌套列表、字典、集合等复杂数据结构,支持递归类型定义和类型参数化。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from pydantic import BaseModelfrom datetime import datetimefrom typing import List , Dict , Optional class Event (BaseModel ): id : int name: str start_time: datetime participants: List [str ] metadata: Optional [Dict [str , str ]] = None event = Event( id ='123' , name='技术研讨会' , start_time='2023-12-01T10:00:00' , participants=['张三' , '李四' ], metadata={'location' : '线上' , 'organizer' : '技术部' } ) print (event.model_dump())
3.3 模型定义与管理 Pydantic 模型是数据结构和业务逻辑的核心载体,支持多种高级特性。
基础模型 :通过继承 BaseModel
创建,字段使用类型注解定义,支持默认值、可选字段和字段校验器。
嵌套模型 :模型字段可以是另一个模型类型,实现数据结构的层级化和模块化,便于处理复杂数据。
继承模型 :支持模型间的继承,子类模型自动继承父类的字段和验证器,也可以覆盖或扩展父类行为。
泛型模型 :支持泛型类型,提高代码复用性,适用于处理不同类型但结构相似的数据。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 from pydantic import BaseModelfrom typing import List , TypeVar, Generic class Person (BaseModel ): name: str age: int class Address (BaseModel ): city: str street: str zip_code: str class Contact (BaseModel ): person: Person address: Address phone: str class Employee (Person ): employee_id: str department: str data_type = TypeVar('data_type' ) class PaginatedResponse (BaseModel, Generic [data_type]): total: int page: int page_size: int items: List [data_type] users = [Person(name='张三' , age=30 ), Person(name='李四' , age=25 )] response = PaginatedResponse[Person](total=2 , page=1 , page_size=10 , items=users) print (response.model_dump())
3.4 与其他库的兼容性 Pydantic 设计为与其他流行 Python 库无缝集成,增强开发体验和效率。
与FastAPI集成 :FastAPI 原生支持 Pydantic 模型,自动用于请求参数验证和响应格式化,大幅减少重复代码。
与Django/Flask集成 :可作为表单验证和API数据处理的补充,在保持框架原生体验的同时提升数据验证能力。
与Pandas/Numpy集成 :支持与数据科学库的数据结构互转,方便在数据处理管道中进行数据验证和清洗。
示例(与FastAPI集成):
1 2 3 4 5 6 7 8 9 10 11 12 13 from fastapi import FastAPIfrom pydantic import BaseModelapp = FastAPI() class Item (BaseModel ): name: str price: float is_offer: bool = None @app.post('/items/' ) async def create_item (item: Item ): return {'item_name' : item.name, 'item_price' : item.price}