Python:小对象只是装数据时,`dataclass` 可以少写很多样板

35 次浏览3 条回复

写那种只负责带几项字段的小对象时,dataclasses.dataclass 还挺舒服的。

环境:Python 3.8+,只用标准库。保存成 dataclass-demo.py 跑:

from dataclasses import dataclass

@dataclass
class Job:
    name: str
    retries: int = 3

job = Job('sync-users')
print(job)
print(job.name)
print(job.retries)

other = Job('sync-users')
print(job == other)

它会自动补上初始化、打印展示和简单比较。小配置、小任务描述这种场景,比手写 __init__ 清爽一点。

这个适合顺手再加个 frozen=True,纯配置对象就不太容易被中途改掉。

from dataclasses import dataclass

@dataclass(frozen=True)
class Job:
    name: str
    retries: int = 3

job = Job('sync-users')
# job.retries = 5  # 这里会报错

环境还是 Python 3.8+,标准库就行。小脚本里拿它当不可变参数包挺舒服。

还有个小补充:如果这种对象会建很多个,可以看看 slots=True。

from dataclasses import dataclass

@dataclass(slots=True)
class Job:
    name: str
    retries: int = 3

job = Job('sync-users')
print(job.name)

环境是 Python 3.10+。它会生成 __slots__,少一点实例字典的开销;不过如果后面想动态往对象上塞新字段,就不适合开这个。

field(default_factory=...) 也挺容易漏掉。字段默认值是 list/dict 这种可变对象时,别直接写 items: list[str] = []。

from dataclasses import dataclass, field

@dataclass
class Job:
    name: str
    tags: list[str] = field(default_factory=list)

a = Job('sync-users')
b = Job('backup')
a.tags.append('nightly')

print(a.tags)
print(b.tags)

环境:Python 3.9+,只用标准库。这样每个实例会拿到自己的新列表,少踩共享默认值的坑。