📢 转载信息
原文链接:https://machinelearningmastery.com/the-complete-guide-to-pydantic-for-python-developers/
原文作者:Bala Priya C
在本文中,您将学习如何使用 Pydantic 在 Python 中利用类型提示来验证、解析和序列化结构化数据。
我们将涵盖的主题包括:
- 使用类型强制转换和清晰的验证错误来定义核心模型
- 有效使用可选字段、默认值和
Field约束 - 编写自定义验证器、处理嵌套结构以及导出 JSON
让我们不要再浪费时间了。
Python 开发者的 Pydantic 完整指南
图片来源:Editor
引言
Python 在处理数据类型方面的灵活性在编码时很方便,但在代码接收到意外的数据格式时,可能会导致运行时错误。当您处理 API、处理配置文件或处理用户输入时,此类错误尤为常见。因此,数据验证对于构建可靠的应用程序变得至关重要。
Pydantic 通过利用 Python 的类型提示系统(您可能已经在使用了),提供了自动数据验证和序列化功能来应对这一挑战,使您能够精确定义数据应有的外观并自动强制执行这些规则。
本文将介绍使用 Pydantic 通过类型提示进行数据验证的基础知识。以下是您将学到的内容:
- 使用类型提示创建和验证数据结构
- 处理可选字段和默认值
- 为特定要求构建自定义验证逻辑
- 处理嵌套模型和复杂数据结构
让我们从基础知识开始。在继续之前,请先安装 Pydantic 库:
pip install pydantic
并跟随示例进行操作。
基础 Pydantic 模型
与需要编写大量 if 语句和类型检查的手动数据验证方法不同,Pydantic 与您现有的 Python 代码集成良好。它使用 Python 的类型提示(您可能已经在使用它们)并将其转化为强大的验证逻辑。
当数据不符合您的规范时,您会收到清晰、可操作的错误消息,而不是晦涩的运行时异常。这减少了调试时间,并使您的代码更易于维护和自我记录。
Pydantic 模型继承自 BaseModel,并使用 Python 类型提示来定义预期的数据结构:
from pydantic import BaseModel
class User(BaseModel):
name: str
age: int
email: str
# Create a user
user = User(name="Alice", age="25", email="alice@example.com")
print(user.age)
print(type(user.age))
输出:
25
此代码定义了一个具有三个必需字段的 User 模型。创建 user 实例时,Pydantic 会自动将字符串 “25” 转换为整数 25。如果转换不可能(例如,为年龄传入 “abc”),它会引发带有清晰错误消息的验证错误,说明哪里出了问题。这种自动类型强制转换在处理所有内容都以字符串形式到达的 JSON 数据或表单输入时尤其有用。
可选字段和默认值
现实世界的数据通常包含缺失或可选的字段。Pydantic 使用 Optional 类型和默认值来处理这种情况:
from pydantic import BaseModel, Field
from typing import Optional
class Product(BaseModel):
name: str
price: float
description: Optional[str] = None
in_stock: bool = True
category: str = Field(default="general", min_length=1)
# All these work
product1 = Product(name="Widget", price=9.99)
product2 = Product(name="Gadget", price=15.50, description="Useful tool")
Optional[str] 类型意味着 description 可以是字符串或 None。带有默认值的字段在创建实例时不需要提供。Field() 函数添加了验证约束。
在这里,它确保 category 至少包含一个字符。这种灵活性允许您的模型优雅地处理不完整的数据,同时仍然强制执行重要的业务规则。
Pydantic 中的自定义验证器
有时您需要的验证逻辑超出了基本的类型检查。验证器允许您实现自定义规则:
from pydantic import BaseModel, field_validator
import re
class Account(BaseModel):
username: str
email: str
password: str
@field_validator('username')
def validate_username(cls, v):
if len(v) < 3:
raise ValueError('Username must be at least 3 characters')
if not v.isalnum():
raise ValueError('Username must be alphanumeric')
return v.lower() # Normalize to lowercase
@field_validator('email')
def validate_email(cls, v):
pattern = r'^[\w\.-]+@[\w\.-]+\.\w+$'
if not re.match(pattern, v):
raise ValueError('Invalid email format')
return v
@field_validator('password')
def validate_password(cls, v):
if len(v) < 8:
raise ValueError('Password must be at least 8 characters')
return v
account = Account(
username="JohnDoe123",
email="john@example.com",
password="secretpass123"
)
验证器在模型创建期间自动运行。它们可以转换数据(例如将用户名转换为小写)或使用描述性错误消息拒绝无效值。
cls 参数允许访问类,v 是正在验证的值。验证器按定义的顺序运行,并且可以访问先前已验证字段的值。
嵌套模型和复杂结构
实际应用程序处理分层数据。Pydantic 使嵌套验证变得简单明了:
from pydantic import BaseModel, field_validator
from typing import List, Optional
from datetime import datetime
class Address(BaseModel):
street: str
city: str
state: str
zip_code: str
@field_validator('zip_code')
def validate_zip(cls, v):
if not v.isdigit() or len(v) != 5:
raise ValueError('ZIP code must be 5 digits')
return v
class Contact(BaseModel):
name: str
phone: str
email: Optional[str] = None
class Company(BaseModel):
name: str
founded: datetime
address: Address
contacts: List[Contact]
employee_count: int
is_public: bool = False
# Complex nested data gets fully validated
company_data = {
"name": "Tech Corp",
"founded": "2020-01-15T10:00:00",
"address": {
"street": "123 Main St",
"city": "San Francisco",
"state": "CA",
"zip_code": "94105"
},
"contacts": [
{"name": "John Smith", "phone": "555-0123"},
{"name": "Jane Doe", "phone": "555-0456", "email": "jane@techcorp.com"}
],
"employee_count": 150
}
company = Company(**company_data)
Pydantic 会递归地验证整个结构。地址会根据 Address 模型规则进行验证,contacts 列表中的每个联系人都作为 Contact 模型进行验证,并且 datetime 字符串会自动解析。如果嵌套结构中有任何部分无效,您将收到一条详细错误消息,确切指出问题所在的位置。
如果一切顺利,company 对象将如下所示:
Company(name='Tech Corp', founded=datetime.datetime(2020, 1, 15, 10, 0), address=Address(street='123 Main St', city='San Francisco', state='CA', zip_code='94105'), contacts=[Contact(name='John Smith', phone='555-0123', email=None), Contact(name='Jane Doe', phone='555-0456', email='jane@techcorp.com')], employee_count=150, is_public=False)
处理 API 和 JSON
Pydantic 在处理通常格式不确定的 API 响应和 JSON 数据方面表现出色。
此示例展示了如何处理典型的 API 挑战:混合数据类型(年龄作为字符串)、各种 datetime 格式和可选字段:
from pydantic import BaseModel, Field, field_validator
from typing import Union, Optional
from datetime import datetime
import json
class APIResponse(BaseModel):
status: str
message: Optional[str] = None
data: Optional[dict] = None
timestamp: datetime = Field(default_factory=datetime.now)
class UserProfile(BaseModel):
id: int
username: str
full_name: Optional[str] = None
age: Optional[int] = Field(None, ge=0, le=150) # Age constraints
created_at: Union[datetime, str] # Handle multiple formats
is_verified: bool = False
@field_validator('created_at', mode='before')
def parse_created_at(cls, v):
if isinstance(v, str):
try:
return datetime.fromisoformat(v.replace('Z', '+00:00'))
except ValueError:
raise ValueError('Invalid datetime format')
return v
# Simulate API response
api_json = '''
{
"status": "success",
"data": {
"id": 123,
"username": "alice_dev",
"full_name": "Alice Johnson",
"age": "28",
"created_at": "2023-01-15T10:30:00Z",
"is_verified": true
}
}
'''
response_data = json.loads(api_json)
api_response = APIResponse(**response_data)
if api_response.data:
user = UserProfile(**api_response.data)
print(f"User {user.username} created at {user.created_at}")
🚀 想要体验更好更全面的AI调用?
欢迎使用青云聚合API,约为官网价格的十分之一,支持300+全球最新模型,以及全球各种生图生视频模型,无需翻墙高速稳定,文档丰富,小白也可以简单操作。
评论区