一、field()的全部参数
field()用于对字段进行精细控制。参数如下:
from dataclasses import dataclass, field @dataclass class Example: a: int = field( default=0, # 默认值 default_factory=None, # 默认工厂(与 default 二选一) init=True, # 是否出现在 __init__ 参数中 repr=True, # 是否出现在 __repr__ 中 compare=True, # 是否参与 __eq__ 比较 hash=None, # 是否参与 __hash__(None 表示继承 compare) metadata=None, # 附加元数据 kw_only=False # 是否强制关键字参数(Python 3.10+) )1.defaultvsdefault_factory
| 参数 | 适用类型 | 说明 |
|---|---|---|
default | 不可变对象 | 数字、字符串、元组、None |
default_factory | 可变对象或动态值 | 列表、字典、集合、函数调用 |
# 不可变默认值:直接用 default count: int = field(default=0) # 可变默认值:必须用 default_factory items: list = field(default_factory=list) # 动态默认值:用工厂函数 created_at: str = field(default_factory=lambda: datetime.now().isoformat())2.init=False:不参与初始化
@dataclass class User: name: str age: int # 不参与 __init__,只能通过其他方式赋值 created_at: str = field(default="", init=False) u = User("Alice", 25) # 不用传 created_at # u = User("Alice", 25, "2024") # 会报错适用场景:字段由内部逻辑填充,不需要外部传入。
3.repr=False:隐藏敏感字段
@dataclass class User: name: str password: str = field(repr=False) # 不出现在 __repr__ 中 u = User("Alice", "secret123") print(u) # User(name='Alice'),密码不会打印适用场景:密码、Token 等敏感信息。
4.compare=False:不参与比较
@dataclass class User: id: int name: str timestamp: float = field(compare=False) # 不参与 == 比较 u1 = User(1, "Alice", 100.0) u2 = User(1, "Alice", 200.0) print(u1 == u2) # True,timestamp 不参与比较适用场景:时间戳、缓存字段等不需要比较的字段。
5.kw_only=True:强制关键字参数(Python 3.10+)
@dataclass class Point: x: int y: int = field(kw_only=True) # 必须用 y= 传 p = Point(1, y=2) # 正确 # p = Point(1, 2) # 报错适用场景:参数多时,强制关键字传参,避免顺序错误。
6.metadata:附加元数据
@dataclass class Product: name: str = field(metadata={"description": "商品名", "max_length": 100}) price: float = field(metadata={"unit": "元"}) # 获取元数据 print(Product.__dataclass_fields__["name"].metadata) # {'description': '商品名', 'max_length': 100}适用场景:配合序列化库、ORM、文档生成工具使用。
二、__post_init__:后置初始化
__init__自动生成后,如果需要在初始化后做额外处理,用__post_init__。
@dataclass class Rectangle: width: float height: float area: float = field(init=False) # 不通过 __init__ 传 def __post_init__(self): self.area = self.width * self.height r = Rectangle(3, 4) print(r.area) # 12.0执行顺序:
自动生成的
__init__赋值所有字段__post_init__被调用完成初始化
常用场景:
计算派生字段
数据校验
类型转换
@dataclass class User: name: str age: int def __post_init__(self): if self.age < 0: raise ValueError("年龄不能为负数") self.name = self.name.strip()三、frozen=True:不可变数据类
frozen=True让实例创建后不能修改,类似tuple。
@dataclass(frozen=True) class Point: x: int y: int p = Point(1, 2) # p.x = 3 # FrozenInstanceError1.frozen=True带来的特性
| 特性 | 说明 |
|---|---|
| 不可修改 | 修改字段抛FrozenInstanceError |
| 可哈希 | 自动生成__hash__,可作为字典键 |
| 线程安全 | 不可变对象天生线程安全 |
p1 = Point(1, 2) p2 = Point(1, 2) d = {p1: "原点"} # 可作为字典键 print(d[p2]) # 原点2.frozen=True的__post_init__陷阱
在frozen=True时,__post_init__里不能用self.x = ...,要用object.__setattr__:
@dataclass(frozen=True) class Circle: radius: float area: float = field(init=False) def __post_init__(self): # self.area = ... # FrozenInstanceError object.__setattr__(self, "area", 3.14 * self.radius ** 2)3. 适用场景
| 场景 | 说明 |
|---|---|
| 配置对象 | 创建后不应修改 |
| 值对象 | 如坐标、金额、颜色 |
| 字典键 | 需要可哈希 |
| 多线程共享 | 避免竞态条件 |
四、order=True:支持排序
order=True自动生成<、<=、>、>=方法,按字段顺序比较。
@dataclass(order=True) class Student: score: int name: str students = [ Student(85, "Bob"), Student(92, "Alice"), Student(78, "Charlie"), ] students.sort() for s in students: print(s) # Student(score=78, name='Charlie') # Student(score=85, name='Bob') # Student(score=92, name='Alice')比较规则:按字段声明顺序,逐字段比较。
@dataclass(order=True) class Item: price: float name: str # 先比 price,price 相同再比 name隐藏字段不参与排序:
@dataclass(order=True) class Item: price: float name: str = field(compare=False) # 不参与排序五、@dataclass的继承
@dataclass支持继承,子类会合并父类的字段。
@dataclass class Person: name: str age: int @dataclass class Student(Person): school: str grade: int s = Student("Alice", 18, "清华", 3) print(s) # Student(name='Alice', age=18, school='清华', grade=3)1. 字段顺序规则
子类的字段会追加到父类字段后面。
@dataclass class A: x: int @dataclass class B(A): y: int # B 的字段顺序:x, y2. 默认值冲突问题
如果父类有默认值,子类字段必须有默认值:
@dataclass class A: x: int = 0 @dataclass class B(A): y: int # 报错:y 没有默认值,但父类 x 有默认值原因:Python 要求有默认值的字段不能在无默认值字段之后。
解决方案:给子类字段也加默认值,或用kw_only=True。
@dataclass class B(A): y: int = 0 # # 或者 @dataclass class C(A): y: int = field(kw_only=True) # 强制关键字参数3.frozen的继承
frozen=True和frozen=False不能混用:
@dataclass(frozen=True) class A: x: int @dataclass(frozen=False) # 报错 class B(A): y: int规则:子类的frozen必须与父类一致。
@dataclassvs Pydantic vs attrs
| 维度 | @dataclass | Pydantic | attrs |
|---|---|---|---|
| 来源 | 标准库 | 第三方 | 第三方 |
| 数据校验 | 无 | 强 | 需配置 |
| 序列化 | 无 | 内置 JSON | 无 |
| 性能 | 快 | 中 | 快 |
| 可变性 | frozen控制 | 可配置 | 可配置 |
| 类型转换 | 无 | 自动 | 需配置 |
| 学习成本 | 低 | 中 | 中 |
| 适用场景 | 内部数据类 | API、配置、验证 | 高性能、复杂需求 |