痛点:写"数据类"重复劳动
# 没有 dataclass:手写
class User:
def __init__(self, name, age, email):
self.name = name
self.age = age
self.email = email
def __repr__(self):
return f"User(name={self.name!r}, age={self.age}, email={self.email!r})"
def __eq__(self, other):
return (self.name, self.age, self.email) == (other.name, other.age, other.email)
dataclass:一行装饰器搞定
from dataclasses import dataclass
@dataclass
class User:
name: str
age: int
email: str
u = User("WadeLy", 30, "hi@x.com")
print(u) # User(name='WadeLy', age=30, email='hi@x.com')
u == User("WadeLy", 30, "hi@x.com") # True
@dataclass 自动生成 __init__、__repr__、__eq__——你只写字段。
默认值 / field()
from dataclasses import dataclass, field
@dataclass
class User:
name: str
age: int = 18 # 简单默认值
skills: list[str] = field(default_factory=list) # 可变默认值要 factory
created_at: str = field(default_factory=lambda: "now")
⚠️ 可变默认值(list / dict)必须用 field(default_factory=...)——不然所有实例共享一份!
冻结的 dataclass:不可变
@dataclass(frozen=True)
class Point:
x: int
y: int
p = Point(1, 2)
p.x = 10 # FrozenInstanceError
冻结后还能放进 set / 当 dict key(frozen 自动加 __hash__)。
TypedDict:dict 的"形状"
适合从 JSON / API 来的字典:
from typing import TypedDict
class User(TypedDict):
name: str
age: int
def show(u: User) -> None:
print(u["name"])
show({"name": "WadeLy", "age": 30}) # OK,是 dict
它本质还是 dict——只是给 mypy 看的形状声明。运行时和普通 dict 一样。
NamedTuple:不可变的、长得像类的元组
from typing import NamedTuple
class Point(NamedTuple):
x: int
y: int
p = Point(3, 4)
print(p.x, p.y) # 3 4
print(p[0], p[1]) # 3 4 也能像元组用
NamedTuple 是元组,不可变 + 可哈希。
三个对比
| 特性 | dataclass | TypedDict | NamedTuple |
|---|---|---|---|
| 底层类型 | 自定义类 | dict | tuple |
| 可变 | ✓(默认) | ✓ | ✗ |
| 可哈希 | frozen=True 才行 | ✗(dict 不可哈希) | ✓ |
| 像元组拆包 | ✗ | ✗ | ✓(x, y = p) |
| 放进 JSON | 要 asdict | ✓(已经是 dict) | 可以 _asdict() |
| 性能 | 中 | 高(就是 dict) | 高(就是 tuple) |
怎么选
- 业务对象,可变:dataclass
- JSON 形状声明:TypedDict
- 坐标 / 区间这种小数据 + 不可变:NamedTuple
- 配置 / 不可变值对象:
@dataclass(frozen=True)
dataclass 进阶:和 mypy 配合
from dataclasses import dataclass
@dataclass
class Order:
items: list[str]
total: float
def add(self, item: str, price: float) -> None:
self.items.append(item)
self.total += price
# mypy 知道 items 是 list[str],total 是 float
# 写错时立刻报警
attrs / Pydantic:功能更强的替代
attrs:dataclass 的"祖宗",功能更多(验证、转换器)Pydantic:FastAPI 标配。dataclass + 数据验证 + JSON 序列化一体。AI 项目几乎必用。
from pydantic import BaseModel
class User(BaseModel):
name: str
age: int
u = User.model_validate_json('{"name": "WadeLy", "age": 30}') # 带验证!
下一篇讲异常体系与自定义异常。