使用dataclass解析XML时列表元素被重复覆盖的问题求助
Dataclass列表元素全部变为最后一个对象的问题解决
问题描述
我用xmltodict将XML文件转换为OrderedDict,并创建dataclass来存储XML的部分内容,部分顶层dataclass包含子对象或重复子对象的列表属性。每个dataclass都实现了@classmethod from_dict方法,输入为OrderedDict并返回自身实例。基础类型属性的取值正常,但处理dataclass列表时出现异常:
from dataclasses import dataclass from typing import List from collections import OrderedDict @dataclass() class Window: name: str # 其他属性... @classmethod def from_dict(cls, input_dict: OrderedDict) -> 'Window': # 原代码可能存在的错误写法(直接修改类属性) cls.name = input_dict.get('name', '') return cls @dataclass() class ContainerClass: windows: List[Window] title: str # 其他属性... @classmethod def from_dict(cls: 'ContainerClass', input_dict: OrderedDict) -> 'ContainerClass': instance = cls instance.title = input_dict.get('title', '') temp_list = [] windows_list = input_dict.get('windows', []) # 返回待解析的odict对象列表(解析后为Window实例) for i in range(0, len(windows_list)): temp_list.append(Window.from_dict(windows_list[i])) print(f"Window name: '{temp_list[i].name}'") # 此打印语句会输出全部10个窗口的名称 instance.windows = temp_list for window in instance.windows: print(f"Window name: '{window.name}'") # 此打印语句会重复输出最后一个窗口的名称10次 return instance
问题表现:调用Window.from_dict时能得到正确的窗口名称,但将列表赋值给instance.windows后,列表中所有元素都变成最后一个窗口对象。尝试过泛型列表和typing.List[Window]类型,均未解决问题(实际存在多层嵌套dataclass)。
核心问题分析
- 未正确实例化
ContainerClass:instance = cls这行代码没有创建类的实例,而是直接将类对象赋值给了instance变量。后续对instance.title、instance.windows的修改都是在修改类的静态属性,而非实例属性。 Window.from_dict方法错误操作类属性:如果Window的from_dict是直接修改cls.name(类属性),而非创建新实例,那么每次调用都会覆盖类的name值,导致所有引用最终指向最后一次设置的内容。
修正后的代码
from dataclasses import dataclass from typing import List from collections import OrderedDict @dataclass() class Window: name: str # 其他属性... @classmethod def from_dict(cls, input_dict: OrderedDict) -> 'Window': # 正确创建Window实例,传入解析后的属性 return cls( name=input_dict.get('name', '') # 其他属性按同样方式解析传入 ) @dataclass() class ContainerClass: windows: List[Window] title: str # 其他属性... @classmethod def from_dict(cls: 'ContainerClass', input_dict: OrderedDict) -> 'ContainerClass': # 解析基础属性 title = input_dict.get('title', '') # 批量解析子对象列表 parsed_windows = [Window.from_dict(window_data) for window_data in input_dict.get('windows', [])] # 通过构造方法创建实例,传入所有必要参数 instance = cls( title=title, windows=parsed_windows ) # 验证输出 for window in instance.windows: print(f"Window name: '{window.name}'") return instance
关键修正点
- 必须实例化类:所有
from_dict方法都要通过cls(...)创建新实例,而非直接操作类本身。 - 遵循dataclass规范:dataclass实例应通过构造方法传入参数创建,尽量避免事后修改属性。
- 简化列表解析逻辑:使用列表推导替代手动循环追加,代码更简洁且减少出错概率。
额外建议
对于多层嵌套的dataclass,确保每个子dataclass的from_dict方法都遵循相同规则——创建新实例而非修改类属性,这样嵌套结构的解析才会正确。
内容的提问来源于stack exchange,提问作者sean1441
相关产品推荐
相关产品推荐

