【问题标题】:What's the idiomatic way to fake __hash__() for dicts?为管道伪造 __hash__() 的惯用方法是什么?
【发布时间】:2012-09-07 19:46:10
【问题描述】:

编辑:正如@BrenBarn 指出的那样,原文没有意义。

给定一个字典列表(由 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 的精神,什么是“显而易见的做法”?

【问题讨论】:

  • 我不确定你在问什么。您是否在寻找a frozen dictionary(即可以用作另一个字典中的键的那个)?您的代码示例没有多大意义(例如,您的第一个示例只是重新创建了原始 pile_o_dicts)。
  • 并不是说这是首选的解决方案或任何东西,但在您的第一个示例中,您可以通过使用 dict 理解来避免评估返回 dict 的“往返”:{repr(d):d for d in pile_o_dicts}.values() 代替一个集合 + 一个带有 evals 的列表比较
  • @BrenBarn 你是对的,编辑成一些(希望!)明智的东西。

标签: python python-3.x


【解决方案1】:

根据您的示例代码,我认为您的问题与您的字面意思略有不同。您实际上并不想覆盖__hash__() - 您只想在线性时间内过滤掉重复项,对吗?因此,您需要为每个字典确保以下内容:1)每个键值对都被表示,2)它们以稳定的顺序表示。您可以使用键值对的排序元组,但我建议使用frozensetfrozensets 是可散列的,它们避免了排序的开销,这应该会提高性能(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

【讨论】:

    【解决方案2】:

    巧妙命名的 pile_o_dicts 可以通过对其 items 列表进行排序来转换为规范形式:

     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,这种技术是经过验证的 :-)

    【讨论】:

    • 很难与标准库竞争。
    【解决方案3】:

    如果字典都具有相同的键,您可以使用namedtuple

    >>> from collections import namedtuple
    >>> nt = namedtuple('nt', pile_o_dicts[0])
    >>> set(nt(**d) for d in pile_o_dicts)
    

    【讨论】:

      猜你喜欢
      • 2011-02-23
      • 2016-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-08
      • 1970-01-01
      • 1970-01-01
      • 2019-03-03
      相关资源
      最近更新 更多