bigywater.tech
Python · 20

dataclass:类装饰器如何自动生成构造函数和比较方法

验证@dataclass自动生成的__init__/__repr__/__eq__三个方法,以及它对可变默认值这个经典陷阱的主动拦截。

0手写方法
自动生成__init__/__repr__/__eq__
主动ValueError
主动拦截可变默认值的经典陷阱
按内容比较
自动生成的__eq__不是比地址

做的事:只声明字段类型,看@dataclass生成了什么

一个普通类要手写__init__/__repr__/__eq__,dataclass号称只要声明字段。

代码@dataclass class Point: x: int y: int label: str = "origin" p1 = Point(1, 2)

验证:__init__、__repr__、__eq__都自动有了

只写了字段声明,三个方法都能用。

结果p1 = Point(1, 2) 自动生成的__repr__: Point(x=1, y=2, label='origin') p2 = Point(1, 2) p1 == p2 (两个不同实例,字段相同): True p1 is p2: False(不是同一个对象,但__eq__按字段比较) 自动生成的__init__签名: (self, x: int, y: int, label: str = 'origin') -> None

验证了:只写了字段类型声明,@dataclass 就生成了一个完整能用的 __init__(连类型注解都保留在签名里)、一个好读的 __repr__、以及一个按字段值逐个比较的 __eq__——p1 == p2 为True但 p1 is p2 为False,正说明 __eq__ 是"比内容",不是普通类默认继承自object的那种"比地址"的相等判断。

验证:dataclass主动拦下了可变默认值的经典陷阱

本系列之前验证过def f(x=[])会导致调用间数据累积——dataclass怎么处理同样的写法。

代码与结果@dataclass class Bad: items: list = [] -- 模仿def f(x=[])的写法 报错: ValueError: mutable default for field items is not allowed: use default_factory

结果是直接报错,而不是像普通函数那样悄悄埋下一个bug——dataclass 在生成 __init__ 的时候,会主动检查默认值是不是list/dict/set这类常见的可变类型,一旦发现就拒绝生成这个类,把问题堵在类定义阶段,而不是等到运行时才在某次调用中暴露出诡异的历史数据。

的正确写法@dataclass class Good: items: list = field(default_factory=list) g1 = Good(); g2 = Good() g1.items.append(1) 结果: g1.items=[1], g2.items=[](互不影响)
这正是本系列前面讲过的"可变默认参数陷阱"的同一个坑,dataclass 用 field(default_factory=...) 提供了标准解法:不是直接给一个共享的默认对象,而是给一个"每次实例化时调用一次"的工厂函数,保证每个实例拿到独立的新对象——跟普通函数里用 if x is None: x = [] 是同一个思路,只是dataclass帮你把它做成了显式的、有报错保护的标准写法。

分步演示:@dataclass做了什么

每一步都是本文上面用Python 3.14跑出来的原始结果。

dataclass自动生成方法,还主动拦下可变默认值陷阱
这一步新验证的结果

    本文用到的工具

    • 本机 Python 3.14.7
    • @dataclass / field(default_factory=...):验证自动生成的方法与陷阱防护
    京ICP备2026067502号-1京公网安备11010502063487号