Python向Set添加Dataclass实例时合并同标识对象属性的方法
问题根因
你当前的实现无法正确归集发言的核心原因有两点:
- 每次循环都创建全新的
Speaker实例,虽然你重写了__eq__和__hash__让相同id+name的实例被判定为相等,但set遇到重复元素时会直接丢弃新传入的实例,不会对已存储的实例做任何更新,自然没法追加新的发言内容 - 循环中单独编写的
Speaker(id, name)没有赋值给任何变量,属于完全无效的冗余代码
推荐实现方案
set本身不支持直接获取已存储的相等元素的引用,因此最高效、且完全兼容后续字段扩展的方案是用临时字典做实例索引(注意这里字典仅存储Speaker实例对象,不会把实例拆成字典结构存储,后续给Speaker新增title等字段时不需要改动核心归集逻辑):
- 字典的key直接使用你判定
Speaker相等的依据,也就是(id, name)元组,如果后续调整相等判定规则,只需要同步修改key的组成即可 - 遍历待处理数据时,如果key不在索引字典中,就新建
Speaker实例存入字典;如果key已存在,直接取出对应实例,往它的statements列表追加当前发言 - 遍历完成后,将字典的所有value转为
set即可得到目标结果
实现代码如下:
from typing import List from dataclasses import dataclass, field @dataclass class Speaker: id: int name: str statements: List[str] = field(default_factory=list) # 后续新增字段直接在这里添加即可,比如title: str = "",归集逻辑无需改动 def __eq__(self, other): return self.id == other.id and self.name == other.name def __hash__(self): return hash((self.id, self.name)) test = [(1, 'john', 'foo'),(1, 'john', 'bar'),(2, 'jane', 'near'),(2, 'george', 'far')] speaker_index = {} for id, name, statement in test: key = (id, name) if key not in speaker_index: speaker_index[key] = Speaker(id=id, name=name) speaker_index[key].statements.append(statement) speakers = set(speaker_index.values()) print(speakers)
运行后输出完全符合预期:
{Speaker(id=1, name='john', statements=['foo', 'bar']), Speaker(id=2, name='jane', statements=['near']), Speaker(id=2, name='george', statements=['far'])}
其他可选方案(不推荐大数据量场景)
如果不想用临时字典,也可以通过遍历set匹配已有实例的方式实现,但是时间复杂度为O(n²),仅适合数据量极小的场景:
speakers = set() for id, name, statement in test: tmp_speaker = Speaker(id, name) # 遍历set查找是否已存在相等的实例 for exist in speakers: if exist == tmp_speaker: exist.statements.append(statement) break else: # 循环正常结束说明没有匹配到已有实例,新建加入 speakers.add(Speaker(id, name, [statement]))
内容的提问来源于stack exchange,提问作者user3517818
相关产品推荐
相关产品推荐

