>>>PyPathPython 学习站
首页›核心篇›kp-020
核心篇 · Core lv.2 核心 kp-020

dataclass 与现代数据建模

前置知识:kp-017、kp-019

1. 一句话定义

@dataclass 装饰器根据类属性注解自动生成 __init__、__repr__、__eq__ 等样板方法,是"以数据为中心的类"的现代默认写法。

2. 为什么重要

手写 __init__ 赋值 10 个字段既啰嗦又易错。dataclass 让"数据记录"的声明只剩字段清单,同时无缝接入类型注解(kp-026);Pydantic 则在其上叠加运行时校验,是 FastAPI/LLM 生态的标配。

3. 前置知识

kp-017、kp-019(理解它"替你写了什么")。

4. 核心概念

python
from dataclasses import dataclass, field

@dataclass
class Book:
    title: str                       # 字段 = 注解(必须带类型注解!)
    price: float
    tags: list[str] = field(default_factory=list)   # 可变默认值的正确姿势
    summary: str = ""                # 普通默认值

b1 = Book("蛇之书", 59.0)
b2 = Book("蛇之书", 59.0)
b1 == b2        # True:自动生成的 __eq__ 按字段比较
print(b1)       # Book(title='蛇之书', price=59.0, tags=[], summary='')

  • 变体:@dataclass(frozen=True) 不可变(可哈希、防误改);slots=True(3.10+)省内存(联动 kp-034)。
  • 兄弟:typing.NamedTuple(不可变元组型记录)、typing.TypedDict(字典形状标注)。
  • Pydantic:运行时校验 + 自动类型转换,BaseModel 适合"外部输入"建模。

5. 原理与机制

dataclass 读取类注解(__annotations__),生成方法代码并注入类——本质是 kp-024 装饰器与 kp-027 元编程的组合应用。"字段即注解"是关键认知:没有注解的字段不是 dataclass 字段。

python
# 验证生成结果
import dataclasses
print(dataclasses.fields(Book))       # 字段元数据可内省
print(Book.__init__.__doc__)

选型心智:

text
内部纯数据记录        → @dataclass(+slots)
不可变的小记录/键     → NamedTuple 或 frozen dataclass
外部输入(HTTP/LLM)  → Pydantic BaseModel(校验)
只需"形状"不必实例化  → TypedDict

6. 关键事实(模型/图示)

text
@dataclass 生成的成员(按序):__init__ → __repr__ → __eq__
frozen=True 追加:__setattr__ 拦截 → __hash__ 自动可用
default_factory:可变默认值的官方解(对应 kp-007 的陷阱)

7. 直观类比

手写类像手工誊写表格:每加一列都要重新画线写表头;dataclass 是电子表格——你只声明列名(字段),行操作(init/repr/eq)由软件保证一致。Pydantic 则是在电子表格上加了"数据验证规则"(年龄必须是正整数)。

8. 实例与案例

python
# 配置对象的现代写法:frozen + slots
@dataclass(frozen=True, slots=True)
class RetryConfig:
    max_attempts: int = 3
    backoff: float = 0.5

cfg = RetryConfig()
# cfg.max_attempts = 5     # FrozenInstanceError —— 配置不可被意外修改

9. 常见误区

  1. 字段注解漏了类型 —— x = 1(赋值无注解)不算字段;x: int = 1 才算。
  2. 可变默认值写成 tags: list = [] —— dataclass 会直接抛 ValueError(比 kp-007 的静默陷阱友善,但要用 field(default_factory=list))。
  3. 把 dataclass 当业务逻辑容器 —— 它适合"数据";行为复杂、状态机式的对象仍该手写类。
  4. 混淆 dataclass 与 Pydantic —— 前者不校验(信你给的值),后者校验外部输入;HTTP 边界用 Pydantic。

10. 自测题

  1. @dataclass 默认生成哪三个方法?frozen=True 额外带来什么?
  2. 为什么 tags: list[str] = [] 会被拒绝?正确写法?
  3. 什么场景选 NamedTuple 而不是 dataclass?
参考答案
  1. __init__、__repr__、__eq__;frozen 额外拦截赋值并使实例可哈希。
  2. 共享可变默认值陷阱(同 kp-007);field(default_factory=list)。
  3. 需要不可变、可解包、可比较且语义为"值"的小记录(如坐标、DB 行),且想省一点内存。

11. 与其他知识点的关系

  • kp-024 装饰器:dataclass 是"会生成代码的装饰器"。
  • kp-026 类型注解:字段注解是数据建模的类型化入口。
  • kp-042/kp-044 Web/AI 生态:Pydantic 是两界共同的输入层。

12. 延伸阅读

  • PEP 557(Dataclasses)
  • Pydantic 文档:https://docs.pydantic.dev/