一、dataclass的进阶用法

Python 3.7引入的dataclass大幅简化了数据类的编写,但真正发挥其威力需要掌握field、__post_init__和slots等特性。

1.1 field工厂与默认值陷阱

from dataclasses import dataclass, field

# ⚠️ 默认值的坑:可变默认值在所有实例间共享
@dataclass
class BadStudent:
    name: str
    scores: list = []  # ❌ 错误!所有实例共享同一个list

s1 = BadStudent("Alice", [90])
s2 = BadStudent("Bob")  # s2.scores也是[],但若:
s1.scores.append(100)  # 改动了默认值列表!
# s2.scores会意外地包含100

# ✅ 正确:使用field(default_factory)
@dataclass
class GoodStudent:
    name: str
    scores: list = field(default_factory=list)
    tags: set = field(default_factory=set)

s1 = GoodStudent("Alice", [90])
s2 = GoodStudent("Bob")
s1.scores.append(100)  # s2不受影响
print(s2.scores)  # []

# field工厂函数(适用于复杂初始化)
@dataclass
class Config:
    # 字典默认值:每次创建新dict
    db_config: dict = field(default_factory=lambda: {
        "host": "localhost",
        "port": 5432,
    })
    # 依赖其他字段的默认值:在__post_init__中处理
    created_at: str = field(default="")

    def __post_init__(self):
        if not self.created_at:
            from datetime import datetime
            self.created_at = datetime.now().isoformat()

1.2 dataclass的slots优化

# Python 3.10+ dataclass支持slots
# 内存优化:每个实例减少约40%内存开销
# 访问优化:属性查找更快(不再通过__dict__)

@dataclass(slots=True)
class OptimizedUser:
    name: str
    email: str
    age: int = 0

# ⚠️ slots=True的限制:
# ① 不能动态添加属性
# ② 不能使用dict解包更新(.replace(**kwargs))
# ③ 不能定义__dict__字段

# 内存对比(100万个实例):
# 普通dataclass: ~350MB
# slots=True:    ~210MB  ← 节省40%

# 适用场景:
# ✅ 数据传输对象(DTO),大量实例
# ✅ 函数返回的临时数据对象
# ❌ 需要动态扩展属性的对象

二、Pydantic V2的核心改进

2.1 BaseModel的改进

from pydantic import BaseModel, Field, field_validator
from typing import Optional
from datetime import datetime

class UserProfile(BaseModel):
    id: int
    name: str = Field(min_length=2, max_length=50)
    email: str
    age: Optional[int] = Field(default=None, ge=0, le=150)

    # 字段验证器(V2)
    @field_validator("email")
    @classmethod
    def validate_email(cls, v: str) -> str:
        if "@" not in v or "." not in v.split("@")[1]:
            raise ValueError("Invalid email format")
        return v.lower()  # 自动小写

    # 模型验证器(所有字段验证后调用)
    @model_validator(mode="after")
    def validate_name_email_diff(self) -> "UserProfile":
        if self.name.lower() in self.email.lower():
            raise ValueError("Email cannot contain name")
        return self

# Pydantic V2的性能提升(核心优化)
# ① Pydantic V2使用Rust实现序列化/反序列化(pydantic-core)
# ② 比V1快50-100倍
# ③ 比dataclass + manual validation快10-20倍

# Benchmark(解析10000个JSON对象):
# Pydantic V1:    450ms
# Pydantic V2:    8ms   ← 56倍提速!
# FastAPI + V2:   12ms

2.2 模型即API Schema

# Pydantic模型直接作为FastAPI的请求/响应Schema
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    name: str
    price: float = Field(gt=0)  # 必须大于0
    quantity: int = Field(ge=1)   # 必须大于等于1

# Pydantic模型 = 请求验证 + JSON Schema自动生成 + 文档
@app.post("/items/")
async def create_item(item: Item) -> Item:
    return {"order_id": 123, **item.model_dump()}

# 自动生成OpenAPI文档:
# 请求体验证 → Pydantic负责
# 响应文档   → 从Pydantic模型推断
# ✅ 无需额外编写Schema定义

# V2新语法:
item_dict = item.model_dump()           # 转为dict
item_json = item.model_dump_json()      # 转为JSON字符串
item_copy = item.model_copy(update={"price": 9.99})  # 浅拷贝+更新

三、嵌套验证与序列化

class Address(BaseModel):
    city: str
    district: str

class Employee(BaseModel):
    name: str
    department: str
    address: Address  # 嵌套自动验证

    model_config = {
        "str_strip_whitespace": True,  # 自动strip字符串空格
    }

# 嵌套数据自动验证
emp = Employee(
    name="张三",
    department="技术部",
    address={"city": "深圳", "district": "南山"}
)
# → 如果city缺失,验证立即失败,无需手动处理

# 自定义序列化器(处理特殊类型)
from pydantic import field_serializer
from datetime import datetime

class LogEntry(BaseModel):
    timestamp: datetime
    level: str
    message: str

    @field_serializer("timestamp")
    def serialize_ts(self, ts: datetime) -> str:
        return ts.strftime("%Y-%m-%d %H:%M:%S")

print(emp.model_dump())
# {'name': '张三', 'timestamp': '2026-04-20 10:30:00', ...}