dataclass子类重写非dataclass父类list属性报AttributeError的解决
dataclass继承非dataclass基类时的可变字段报错问题
前置硬性约束
- 基类
A不得定义为dataclass - 子类
B必须继承自类A - 类
A实例化时不允许传入参数a - 类
B实例化时必须支持传入参数a
基础实现参考
最初的传统类实现代码如下,未使用dataclass特性:
from typing import List class A: a: List[int] = [] class B(A): def __init__(self, b: str, a: List[int] = []): self.b = b self.a = a
踩坑尝试与报错记录
开发者最初尝试将类B改造为dataclass,预期可简化构造方法编写,编写的第一版改造代码如下:
from typing import List from dataclasses import dataclass, field class A: a: List[int] = [] @dataclass class B(A): b: str a: List[int]
运行代码时抛出ValueError: mutable default <class 'list'> for field a is not allowed: use default_factory错误(注:原问题描述中报错提示出现的babies为代码笔误,实际对应重名的a字段)。
按照报错提示,开发者尝试将类A中a属性的定义替换为field(default_factory=list),修改后代码如下:
from typing import List from dataclasses import dataclass, field class A: a: List[int] = field(default_factory=list) @dataclass class B(A): b: str a: List[int]
修改后再次运行,抛出AttributeError: a错误。
为排查问题,开发者开展对照测试:将a的类型从List[int]替换为int类型,相同写法可正常运行,证明整体实现思路可行,仅存在写法适配问题,对照测试代码如下:
from typing import List from dataclasses import dataclass, field class A: a: int = 1 @dataclass class B(A): b: str a: int
问题核心原因
两类报错都是dataclass的内置规则触发的,没有复杂的语法问题:
- 第一版改造代码的
ValueError:dataclass在收集类字段时,会扫描所有基类的注解属性,当发现可变类型(如list)的属性直接作为字段默认值时,会直接触发安全校验拦截——这类可变默认值会导致所有类实例共享同一个内存地址,引发隐蔽bug,因此dataclass不允许直接使用可变对象作为字段默认值。 - 对照测试中int类型可以正常运行的原因:int是不可变类型,就算所有实例共享同一个默认值,也不会出现共享可变状态的问题,因此dataclass不会触发拦截。
- 第二版改造代码的
AttributeError:在非dataclass的类A上直接赋值field(default_factory=list),这个field对象会被当作普通的类属性存储,不会被dataclass机制识别为字段的元数据配置。当类B的dataclass生成字段列表时,尝试解析同名字段的默认值,就会因为读取到未初始化的field对象触发属性错误。
可运行的合规实现
不需要把类A改成dataclass,只需要在类B的dataclass声明中,为a字段显式指定合法的可变默认值规则即可。注意字段声明顺序要符合dataclass规则:无默认值的字段(如b: str)必须排在有默认值的字段前面,避免触发字段排序校验错误。调整后的代码完全满足所有硬性约束:
from typing import List from dataclasses import dataclass, field # 类A保持非dataclass状态,仅持有类级别的a属性默认值,使用默认构造方法 class A: a: List[int] = [] # 类B继承自A,通过dataclass生成标准化构造方法 @dataclass class B(A): b: str # 显式声明a字段的规则:支持传入自定义a值,不传时自动生成空列表作为默认值 a: List[int] = field(default_factory=list)
约束匹配验证
- 类A未添加
@dataclass装饰器,满足“类A不能定义为dataclass”的要求 - 类B在定义时显式继承类A,满足继承要求
- 类A使用Python默认的object构造方法,实例化
A()时无法传入a参数,满足“类A实例化时不允许传入参数a”的要求 - 类B的dataclass自动生成带参数的构造方法,实例化时可传入自定义的a列表值(例如
B(b="test", a=[1,2,3])),满足“类B实例化时需要支持传入参数a”的要求
内容的提问来源于stack exchange,提问作者Alexander McFarlane
相关产品推荐
相关产品推荐

