@dataclass依据字段注解自动生成__init__/__repr__/__eq__,用几行声明替代成片的样板代码。
基本用法
from dataclasses import dataclass
@dataclass
class Point:
x: float
y: float = 0.0等价于手写:
class Point:
def __init__(self, x: float, y: float = 0.0):
self.x = x
self.y = y
def __repr__(self): ... # Point(x=..., y=...)
def __eq__(self, other): ... # 逐字段比较字段顺序即 __init__ 参数顺序,无默认值的字段必须在前。字段本质是 类属性与实例属性 的自动化管理——类体上只留注解与默认值,实例属性在生成的 __init__ 里赋上。
可变默认值:default_factory
默认参数在定义时求值一次并被共享,直接写 items: list = [] 会让所有实例共享同一列表。dataclass 干脆禁止这种写法,必须用工厂函数:
from dataclasses import dataclass, field
@dataclass
class Trainer:
lr: float = 1e-3
history: list[float] = field(default_factory=list) # 每实例新列表
def log(self, loss: float):
self.history.append(loss)field() 另有 init=False(不进构造参数)、repr=False(不进 repr)等开关。
frozen / slots / kw_only
@dataclass(frozen=True, slots=True, kw_only=True)
class Config:
name: str
version: str = "1.0"frozen=True:实例不可变,赋值抛FrozenInstanceError;同时生成__hash__,可作 dict 键、可入 setslots=True(3.10+):生成__slots__,省去实例__dict__,大批量对象内存占用显著下降(原理见 属性访问与查找)kw_only=True(3.10+):参数强制关键字传递,未来加字段不破坏位置参数兼容性
post_init 钩子
生成的 __init__ 末尾自动调用,做派生字段与校验:
@dataclass
class Rect:
w: float
h: float
area: float = field(init=False)
def __post_init__(self):
if self.w < 0 or self.h < 0:
raise ValueError("边长不能为负")
self.area = self.w * self.h与 NamedTuple / attrs 取舍
| 方案 | 特点 |
|---|---|
@dataclass | 标准库;hooks、frozen/slots/kw_only 参数化最全 |
NamedTuple | 不可变、tuple 子类、可解包、更省内存 |
attrs | 第三方;validator/converter 更强,dataclass 的前身 |
不可变的值对象优先 NamedTuple;需要 __post_init__ 与各种开关组合用 dataclass;重度校验上 attrs 或 Pydantic。
生成的 __init__/__repr__/__eq__ 全是 特殊方法,任何一个都可手动覆盖。字段类型注解遵循 类型注解 typing,且是 dataclass 识别字段的唯一依据——没有注解的类属性不会成为字段。