dataclass是 Python 3.7 引入的一个装饰器位于dataclasses模块用于自动生成类中常见的特殊方法如__init__、__repr__、__eq__、__hash__等从而减少编写样板代码的工作量让类更专注于数据存储。1. 为什么需要 dataclass在传统 Python 类中定义一个存储数据的类需要手动编写__init__方法甚至还要实现__repr__等方法class Person: def __init__(self, name: str, age: int): self.name name self.age age def __repr__(self): return fPerson(name{self.name!r}, age{self.age!r}) def __eq__(self, other): if not isinstance(other, Person): return False return self.name other.name and self.age other.age使用dataclass后只需声明字段from dataclasses import dataclass dataclass class Person: name: str age: intPython 会自动生成__init__、__repr__、__eq__等方法代码简洁且不易出错。2. 基本用法2.1 声明字段字段需要类型注解Python 3.6 支持变量注解。没有默认值的字段必须放在有默认值的字段之前。dataclass class Point: x: float y: float label: str origin # 有默认值2.2 创建实例p Point(3.0, 4.0) print(p) # Point(x3.0, y4.0, labelorigin) print(p.x, p.y) # 3.0 4.02.3 自动生成的方法__init__(self, x, y, labelorigin)初始化字段。__repr__(self)返回Point(x3.0, y4.0, labelorigin)。__eq__(self, other)比较所有字段是否相等。__hash__如果frozenTrue或设置unsafe_hashTrue。__post_init__(self)初始化后钩子可用于字段验证或派生字段。3. 主要参数dataclass装饰器接受以下参数默认值如括号内参数说明initTrue是否生成__init__方法。reprTrue是否生成__repr__方法。eqTrue是否生成__eq__方法。orderFalse是否生成比较方法__lt__、__le__、__gt__、__ge__。若为True字段会按声明顺序比较。unsafe_hashFalse是否生成__hash__方法。默认情况下如果eqTrue且frozenFalse则__hash__为None不可哈希。设为True会基于字段生成哈希。frozenFalse若为True实例变为不可变类似namedtuple赋值会引发FrozenInstanceError。4. 字段高级特性使用dataclasses.field()可以自定义每个字段的行为from dataclasses import dataclass, field dataclass class Student: name: str grades: list field(default_factorylist) # 默认值必须是不可变或使用 default_factory id: int field(initFalse) # 不在 __init__ 中出现需在 __post_init__ 中赋值 created_at: float field(default_factorytime.time, compareFalse) # 不参与比较field()参数参数说明default字段的默认值必须是不可变对象。default_factory返回默认值的可调用对象用于可变默认值如列表、字典。initTrue是否出现在__init__参数中。reprTrue是否出现在__repr__中。compareTrue是否参与比较__eq__等。hashTrue是否参与哈希若unsafe_hashTrue。metadata元数据字典可被外部库使用。5. 后初始化钩子__post_init__在__init__执行完后自动调用可用于校验、派生字段等dataclass class Rectangle: width: float height: float area: float field(initFalse) def __post_init__(self): self.area self.width * self.height if self.width 0 or self.height 0: raise ValueError(尺寸必须为正数)6. 继承dataclass 支持继承。子类会继承父类的所有字段并自动合并字段列表。dataclass class Base: x: int dataclass class Derived(Base): y: int d Derived(1, 2) # __init__(x, y)注意如果子类和父类有相同字段名会出现字段覆盖警告或错误取决于 Python 版本。7. 可变与不可变默认情况下 dataclass 实例是可变的可以修改字段值。设置frozenTrue后实例不可变尝试修改会引发异常。dataclass(frozenTrue) class ImmutablePoint: x: int y: int p ImmutablePoint(1, 2) p.x 3 # FrozenInstanceError8. 实用技巧8.1 将 dataclass 转换为字典或元组from dataclasses import asdict, astuple p Point(1, 2) print(asdict(p)) # {x: 1, y: 2} print(astuple(p)) # (1, 2)8.2 替换字段值创建副本from dataclasses import replace p1 Point(1, 2) p2 replace(p1, x3) # Point(3, 2)8.3 仅比较部分字段通过field(compareFalse)禁用某些字段的比较。dataclass class User: name: str password: str field(reprFalse, compareFalse)8.4 使用InitVar传递仅初始化使用的变量from dataclasses import dataclass, InitVar dataclass class C: i: int j: int None database: InitVar[dict] None def __post_init__(self, database): if self.j is None and database is not None: self.j database.get(j, 0)9. 常见错误可变默认值不能写field(default[])应使用field(default_factorylist)。字段顺序无默认值的字段不能出现在有默认值字段之后。类型注解虽然 Python 不强制类型检查但 dataclass 依赖类型注解来识别字段。如果没有注解该属性不会被当作字段。