【问题标题】:Strange Python set and hash behaviour - how does this work?奇怪的 Python 集合和哈希行为 - 这是如何工作的?
【发布时间】:2010-01-29 01:02:43
【问题描述】:

我有一个名为 GraphEdge 的类,我希望通过其 tailhead 成员在一个集合(内置 set 类型)中唯一定义它,这些成员是通过 __init__ 设置的.

如果我没有定义 __hash__,我会看到以下行为:

>>> E = GraphEdge('A', 'B')
>>> H = GraphEdge('A', 'B')
>>> hash(E)
139731804758160
>>> hash(H)
139731804760784
>>> S = set()
>>> S.add(E)
>>> S.add(H)
>>> S
set([('A', 'B'), ('A', 'B')])

集合无法知道 EH 根据我的定义是相同的,因为它们具有不同的哈希值(据我所知,这是集合类型用来确定唯一性的方法),所以它添加了两者都是不同的元素。所以我为GraphEdge 定义了一个相当幼稚的哈希函数,如下所示:

def __hash__( self ):
    return hash( self.tail ) ^ hash( self.head )

现在上述工作按预期工作:

>>> E = GraphEdge('A', 'B')
>>> H = GraphEdge('A', 'B')
>>> hash(E)
409150083
>>> hash(H)
409150083
>>> S = set()
>>> S.add(E)
>>> S.add(H)
>>> S
set([('A', 'B')])

但很明显,('A', 'B')('B', 'A') 在这种情况下将返回相同的哈希值,所以我希望我不能将 ('B', 'A') 添加到已经包含 ('A', 'B') 的集合中。但事实并非如此:

>>> E = GraphEdge('A', 'B')
>>> H = GraphEdge('B', 'A')
>>> hash(E)
409150083
>>> hash(H)
409150083
>>> S = set()
>>> S.add(E)
>>> S.add(H)
>>> S
set([('A', 'B'), ('B', 'A')])

那么集合类型是否使用散列?如果是这样,最后一种情况怎么可能?如果不是,为什么第一个场景(没有定义__hash__)不起作用?我错过了什么吗?

编辑:供未来读者参考,我已经定义了__eq__(也基于tailhead)。

【问题讨论】:

    标签: python hash set


    【解决方案1】:

    你有一个哈希冲突。在哈希冲突时,该集合使用 == 运算符来检查它们是否真正彼此相等。

    【讨论】:

    • 啊,有道理。总是简单地返回 0 作为哈希值以强制设置为使用 == 运算符会更好吗?我看不出它会比散列函数本身引入更多的开销。
    • 确保每个对象都会发生哈​​希冲突,这肯定会影响您的性能。哈希用于归档集合内的对象。
    • @jleedev:注意,谢谢。现在使用更好的哈希函数。
    【解决方案2】:

    了解 hash 和 == 如何协同工作很重要,因为两者都被集合使用。对于两个值 x 和 y,重要的规则是:

    x == y ==> hash(x) == hash(y)
    

    (x 等于 y 意味着 x 和 y 的哈希值相等)。但是,反之则不然:两个不相等的值可以有相同的哈希值。

    Sets(和 dicts)将使用散列来获得相等的近似值,但将使用真正的相等运算来确定两个值是否相同。

    【讨论】:

      【解决方案3】:

      如果您至少需要其中一个,则应始终定义__eq__()__hash__()。如果两个对象的哈希值相等,则会进行额外的__eq__() 检查以验证唯一性。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-18
        • 2016-03-17
        • 2010-10-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多