Python - Pydantic基于 type hints 类型注解进行数据验证

Python - Pydantic基于 type hints 类型注解进行数据验证

flyfish

在 Python 编程里,type hints(类型提示)是一项关键特性,它能为变量、函数参数、返回值等添加类型注解,从而提升代码的可读性

概念解析

  • Type:指数据类型,像 intstrlist、自定义类等都属于数据类型。
  • Hints:即提示,它并非强制要求,而是给开发者和工具(如 IDE、静态检查工具)提供类型信息。

主要作用

  1. 提升代码可读性:通过类型提示,能清晰地看出变量或函数的预期类型。
# 不使用类型提示
def add(a, b):
    return a + b

# 使用类型提示
def add(a: int, b: int) -> int:
    return a + b
  1. 辅助 IDE 和静态分析:帮助 IDE 进行代码补全、类型检查,提前发现潜在错误。
  2. 文档自动生成:类型信息可以直接作为接口文档,减少手动编写文档的工作量。
  3. 代码维护:明确类型约束,降低因类型不匹配而引发的错误。

基础语法

1. 变量类型提示
name: str = "Alice"  # 字符串类型
age: int = 30        # 整数类型
is_student: bool = True  # 布尔类型
2. 函数参数与返回值
def greet(name: str) -> str:
    return f"Hello, {name}"

def calculate_total(prices: list[float]) -> float:
    return sum(prices)
3. 复杂类型(使用 typing 模块)
from typing import List, Dict, Optional, Union

# 列表元素类型为字符串
names: List[str] = ["Alice", "Bob"]

# 字典键为字符串,值为整数
person: Dict[str, int] = {"age": 30, "score": 95}

# 可选类型(可以是字符串或者 None)
address: Optional[str] = None

# 联合类型(可以是整数或者浮点数)
number: Union[int, float] = 3.14
4. 自定义类型
class Point:
    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y

def distance(p1: Point, p2: Point) -> float:
    return ((p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2) ** 0.5
5. 泛型与类型别名
from typing import TypeVar, List

T = TypeVar('T')  # 定义泛型类型

def first_element(items: List[T]) -> T | None:
    return items[0] if items else None

# 类型别名
Vector = List[float]

def scale(vector: Vector, factor: float) -> Vector:
    return [x * factor for x in vector]

与 Pydantic 的结合应用

Pydantic 以类型提示为基础进行数据验证,两者结合使用能发挥强大的威力:

from pydantic import BaseModel
from typing import List, Optional

class Book(BaseModel):
    title: str
    author: str
    year: int
    tags: Optional[List[str]] = []  # 可选的字符串列表

# 自动验证类型
book = Book(title="Python Crash Course", author="Eric Matthes", year=2015)

# 类型错误会引发验证异常
try:
    Book(title=123, author="Invalid", year="2023")
except ValueError as e:
    print(e)  # 输出: "title" 应该是字符串类型,"year" 应该是整数类型

应用场景

  1. 函数参数和返回值说明
def process_data(data: dict[str, list[int]]) -> tuple[float, float]:
    """处理数据并返回平均值和最大值"""
    values = [item for sublist in data.values() for item in sublist]
    return sum(values)/len(values), max(values)
  1. 类属性类型定义
class User:
    name: str
    age: int
    hobbies: list[str]

    def __init__(self, name: str, age: int, hobbies: list[str]):
        self.name = name
        self.age = age
        self.hobbies = hobbies
  1. 集合元素类型声明
students: list[dict[str, str | int]] = [
    {"name": "Alice", "age": 20},
    {"name": "Bob", "age": 22}
]

Python 是动态类型语言,类型提示不会影响程序的运行,只是起到辅助作用。
类型提示在运行时会被忽略,若需要强制类型检查,可借助 Pydantic 等工具。
Python 3.9 及以后的版本可以直接使用内置的容器类型(如 listdict),无需从 typing 模块导入。

BaseModel

BaseModel 的核心功能之一就是通过 Python 类型提示(Type Hints) 来定义和验证数据结构。不过,它的功能远不止于此。

1. 数据验证与类型转换

  • 自动类型转换:将输入数据转换为声明的类型(如字符串转整数、列表转模型列表)。
  • 约束检查:通过 Field 设置最小值、最大值、长度限制等。
  • 自定义验证器:使用 @validator 装饰器编写复杂逻辑。
from pydantic import BaseModel, Field, validator

class User(BaseModel):
    id: int
    name: str = Field(min_length=2, max_length=50)
    age: int = Field(ge=0, le=150)  # 年龄范围 0-150

    @validator('name')
    def validate_name(cls, v):
        if v.isdigit():
            raise ValueError('名字不能全为数字')
        return v

2. 序列化与反序列化

  • 对象 → 字典/JSON:通过 model.dict()model.json() 导出数据。
  • 字典/JSON → 对象:通过 Model(**data)Model.parse_obj(data) 导入数据。
  • 支持嵌套结构:自动处理复杂的嵌套模型。
class Address(BaseModel):
    city: str
    street: str

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

# 嵌套数据反序列化
user = UserWithAddress.parse_obj({
    "name": "Alice",
    "address": {"city": "Beijing", "street": "Main St"}
})

# 序列化
print(user.json(indent=2))

3. 配置管理

  • 通过 Config 类自定义模型行为:
    • extra = 'forbid':禁止额外字段。
    • validate_assignment = True:赋值时验证(动态修改字段时触发验证)。
    • orm_mode = True:支持从 ORM 对象创建模型。
class Settings(BaseModel):
    api_key: str
    debug: bool = False

    class Config:
        env_file = ".env"  # 从环境变量加载
        allow_mutation = False  # 禁止修改字段

4. 错误处理与异常

  • 验证失败时抛出详细的 ValidationError,包含错误位置和原因。
  • 支持多语言错误信息(如中文、英文)。
try:
    User(id="invalid", name="A", age=-5)
except ValidationError as e:
    print(e.json(indent=2))
    """输出:
    [
        {
            "loc": ["id"],
            "msg": "value is not a valid integer",
            "type": "type_error.integer"
        },
        ...
    ]
    """

5. 字段默认值与可选值

  • 使用 NoneOptional 定义可选字段。
  • 使用 Field(default_factory) 设置动态默认值(如生成唯一 ID)。
from uuid import UUID, uuid4
from typing import Optional

class Item(BaseModel):
    id: UUID = Field(default_factory=uuid4)  # 自动生成 UUID
    name: str
    description: Optional[str] = None  # 可选字段

6. 计算属性(@property)

  • 通过 Python 的 @property 装饰器定义只读字段。
  • 使用 @computed_field(Pydantic v2)定义更灵活的计算字段。
class Rectangle(BaseModel):
    width: float
    height: float

    @property
    def area(self) -> float:
        return self.width * self.height

7. 模型继承与多态

  • 支持模型继承,复用字段和验证逻辑。
  • 通过 Uniondiscriminator 实现多态。
from typing import Union

class Animal(BaseModel):
    name: str

class Dog(Animal):
    breed: str

class Cat(Animal):
    is_indoor: bool

class Zoo(BaseModel):
    animals: list[Union[Dog, Cat]]  # 支持多种动物类型

8. 运行时类型检查

  • 虽然 Python 的类型提示在运行时被忽略,但 Pydantic 会在运行时强制执行类型验证。

你可能感兴趣的:(#,Python,python,Pydantic)