如何在python中实现一个好的__hash__函数[重复]

Posted

技术标签:

【中文标题】如何在python中实现一个好的__hash__函数[重复]【英文标题】:How to implement a good __hash__ function in python [duplicate] 【发布时间】:2011-04-29 15:38:50 【问题描述】:

在实现具有多个属性的类时(如下面的玩具示例),处理散列的最佳方法是什么?

我猜__eq____hash__应该是一致的,但是如何实现一个能够处理所有属性的合适的散列函数呢?

class AClass:
  def __init__(self):
      self.a = None
      self.b = None

  def __eq__(self, other):
      return other and self.a == other.a and self.b == other.b

  def __ne__(self, other):
    return not self.__eq__(other)

  def __hash__(self):
      return hash((self.a, self.b))

我在this question 上读到元组是可散列的,所以我想知道上面的例子是否合理。是吗?

【问题讨论】:

只要确保在一个元组上使用 hash()__eq__() 和朋友中比较的元素完全相同(就像你所做的那样),你就可以开始了。 【参考方案1】:

object.__hash__(self) 的文档

唯一需要的属性是比较相等的对象具有相同的哈希值;建议以某种方式混合在一起(例如,使用异或)对象组件的哈希值,这些组件也参与对象的比较。

def __hash__(self):
    return hash(self.a) ^ hash(self.b)

【讨论】:

它会起作用,但如果你交换self.aself.b,那么你会得到相同的哈希值,而它将是另一个“对象”。 “以某种方式混合在一起(例如,使用异或”是一组非常灵活的要求。如果它真的很重要,那么(hash(self.a)<<1) ^ hash(self.b) 可能会更好。没有一般的答案,只有一般的指导方针根据具体应用进行修改。 为什么不只是散列一个元组值?哈希((self.a,self.b)) 请注意(幸运的是)Python 3 或 Python 2 文档中不再存在使用 xor 的建议。 对于那些感兴趣的人,这里是导致删除 XOR 推荐的错误:bugs.python.org/issue28383【参考方案2】:

__hash__ 应该为相等的对象返回相同的值。它也不应该在对象的生命周期内改变;通常你只为不可变对象实现它。

一个简单的实现就是return 0。这总是正确的,但性能很差。

您的解决方案(返回属性元组的哈希)很好。但请注意,您不需要在元组中列出您在 __eq__ 中比较的所有属性。如果某些属性通常对不相等的对象具有相同的值,则将其省略。不要使散列计算的开销超出其所需。

编辑:我建议不要使用 xor 来混合散列。当两个不同的属性具有相同的值时,它们将具有相同的哈希值,并且通过 xor 这些将相互抵消。元组使用更复杂的计算来混合哈希,参见tupleobject.c 中的tuplehash

【讨论】:

正如你所说,哈希函数通常只对不可变对象有意义。因此可以在__init__ 中计算一次哈希值。 +1 用于return 0 哈希函数——我一直认为其他任何事情都是过早的优化:-)。 (我只是在开玩笑)。 @BjörnPollex 而不是在__init__ 中执行此操作,您可以将值缓存在__hash__ 中。这样,如果从未调用过__hash__,您就不会浪费时间或内存。我假设检查值是否已经被缓存并不昂贵,是吗? (不确定最好是通过异常还是显式if)。 很遗憾,Python 没有提供combine_hashes 函数。 它没有在 dict 或 list 之类的东西中实现,理由是更改已经属于的对象的哈希值,例如,集合会对集合的内部数据结构造成严重破坏。【参考方案3】:

写作很危险

def __eq__(self, other):
  return other and self.a == other.a and self.b == other.b

因为如果您的 rhs(即 other)对象的计算结果为布尔值 False,它永远不会与任何东西相比较!

此外,您可能需要仔细检查other 是否属于AClass 的类或子类。如果没有,您将得到异常AttributeError 或误报(如果另一个类碰巧具有具有匹配值的同名属性)。所以我建议将__eq__ 重写为:

def __eq__(self, other):
  return isinstance(other, self.__class__) and self.a == other.a and self.b == other.b

如果您想要一个异常灵活的比较,只要属性按名称匹配就可以在不相关的类之间进行比较,您仍然希望至少避免 AttributeError 并检查 other 是否没有任何附加属性。具体怎么做取决于具体情况(因为没有找到对象所有属性的标准方法)。

【讨论】:

有用的信息,但与散列的主要问题无关。 不相关,但非常感谢您发布此内容。 +1。 这是一个糟糕的__eq__ 实现,因为如果左手不知道如何进行比较,它不会委托给右手边的__eq__。如果int 有这样的__eq__1 == MyNumericType(1) 将始终为False,即使MyNumericType(1) == 1 将返回True。如果你不认识other 的类型,总是return NotImplemented,不要只是return False @ShadowRanger 同意。

以上是关于如何在python中实现一个好的__hash__函数[重复]的主要内容,如果未能解决你的问题,请参考以下文章

如何在Python中实现二进制搜索树?

在 Python 中实现事件处理程序 [重复]

python中实现可迭代对象的方法

如何在机器人框架中实现并行执行

Python中实现iterator

如何在一个类中实现 asyncio websockets?