给出一个dicts列表(由csv.DictReader
提供 - 它们都有str
个键和值),通过将它们全部填入一个集合来删除重复项是很好的,但这可以'由于dict
不可清,因此可以直接完成。一些existing questions触及了如何伪造__hash__()
的集合/词组,但没有说明应首选的方式。
# i. concise but ugly round trip
filtered = [eval(x) for x in {repr(d) for d in pile_o_dicts}]
# ii. wordy but avoids round trip
filtered = []
keys = set()
for d in pile_o_dicts:
key = str(d)
if key not in keys:
keys.add(key)
filtered.append(d)
# iii. introducing another class for this seems Java-like?
filtered = {hashable_dict(x) for x in pile_o_dicts}
# iv. something else entirely
本着Zen of Python的精神,“明显的做法”是什么?
答案 0 :(得分:4)
根据您的示例代码,我会将您的问题与您的字面意思略有不同。您实际上并不想覆盖__hash__()
- 您只想在线性时间内过滤掉重复项,对吧?因此,您需要为每个字典确保以下内容:1)表示每个键值对,以及2)它们以稳定的顺序表示。您可能使用已排序的键值对元组,但我建议您使用frozenset
。 frozenset
是可以清除的,它们可以避免排序的开销,这可以提高性能(因为this answer似乎已经确认)。缺点是它们占用的内存比元组多,所以这里有一个空间/时间权衡。
此外,您的代码使用集合来进行过滤,但这并没有多大意义。如果您使用字典,则不需要那个丑陋的eval
步骤:
filtered = {frozenset(d.iteritems()):d for d in pile_o_dicts}.values()
或者在Python 3中,假设您需要列表而不是字典视图:
filtered = list({frozenset(d.items()):d for d in pile_o_dicts}.values())
这些都很笨拙。为了便于阅读,请考虑将其分为两行:
dict_o_dicts = {frozenset(d.iteritems()):d for d in pile_o_dicts}
filtered = dict_o_dicts.values()
替代方案是一个有序的元组元组:
filtered = {tuple(sorted(d.iteritems())):d for d in pile_o_dicts}.values()
最后一点:不要使用repr
。评估为相等的字典可以有不同的表示形式:
>>> d1 = {str(i):str(i) for i in range(300)}
>>> d2 = {str(i):str(i) for i in range(299, -1, -1)}
>>> d1 == d2
True
>>> repr(d1) == repr(d2)
False
答案 1 :(得分:3)
巧妙命名的pile_o_dicts可以通过对项目列表进行排序来转换为规范形式:
groups = {}
for d in pile_o_dicts:
k = tuple(sorted(d.items()))
groups.setdefault(k, []).append(d)
这会将相同的词典组合在一起。
FWIW,使用sorted(d.items())
的技术目前在functools.lru_cache()的标准库中使用,以便识别具有相同关键字参数的函数调用。 IOW,这种技术是经过验证的: - )
答案 2 :(得分:2)
如果dicts都具有相同的键,则可以使用namedtuple
>>> from collections import namedtuple
>>> nt = namedtuple('nt', pile_o_dicts[0])
>>> set(nt(**d) for d in pile_o_dicts)