如何在Python中实现一个好的__hash__函数

138 投票
3 回答
123071 浏览
提问于 2025-04-16 05:58

在实现一个有多个属性的类时(就像下面这个简单的例子),处理哈希的最佳方法是什么呢?

我觉得 __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))

我在这个问题上看到元组是可以哈希的,所以我在想像上面的例子这样做是否合理。这样做可以吗?

3 个回答

22

写这样的代码是有风险的

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

因为如果你的右边的对象(也就是 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 是否有额外的属性。具体怎么做要看情况(因为没有标准的方法可以找到一个对象的所有属性)。

30

这是关于 object.__hash__(self) 的说明

唯一需要注意的地方是:如果两个对象比较后是相等的,它们的哈希值也必须相同。建议把对象中参与比较的各个部分的哈希值混合在一起,可以把这些值放进一个元组里,然后对这个元组进行哈希处理。举个例子:

def __hash__(self):
    return hash((self.name, self.nick, self.color))
104

__hash__ 方法应该为相等的对象返回相同的值。而且,这个值在对象的整个生命周期内都不应该改变;通常情况下,你只会为不可变对象实现这个方法。

一个简单的实现方式就是直接 return 0。这样做总是正确的,但性能很差。

你提到的方案,通过返回一个属性元组的哈希值,是个不错的选择。不过要注意,你不需要在元组中列出所有在 __eq__ 中比较的属性。如果某个属性在不相等的对象中通常值是相同的,就可以不把它放进去。不要让哈希计算变得比必要的更复杂。

补充:我不建议一般情况下使用异或(xor)来混合哈希值。当两个不同的属性有相同的值时,它们会有相同的哈希值,而使用异或会让它们相互抵消。元组使用更复杂的计算来混合哈希值,具体可以查看 tuplehashtupleobject.c 中的实现。

撰写回答