如何在 python 中实现一个好的__ hash__ 函数

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

我想 __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))

我在 这个问题上读到元组是散列的,所以我想知道上面的例子是否合理。是吗?

103908 次浏览

Documentation for object.__hash__(self)

唯一需要的属性是比较相等的对象具有相同的哈希值; 建议将对象组件的哈希值混合在一起,这些组件也在比较对象时起作用,方法是将它们打包成一个元组并对元组进行哈希处理。例子

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

对于相等的对象,__hash__应该返回相同的值。它也不应该在对象的生存期内更改; 通常只对不可变对象实现它。

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

您的解决方案,返回一个元组属性的散列,是很好的。但是请注意,您不需要在元组中列出 __eq__中比较的所有属性。如果某个属性通常对不等的对象具有相同的值,那么就不要使用它。不要使散列计算的开销高于实际需要的开销。

Edit: I would recommend against using xor to mix hashes in general. When two different properties have the same value, they will have the same hash, and with xor these will cancel eachother out. Tuples use a more complex calculation to mix hashes, see tuplehash in tupleobject.c.

写作是很危险的

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

因为如果您的 rhs (即 other)对象的计算结果为 boolean 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是否有任何其他属性。如何做取决于具体情况(因为没有标准的方法来查找对象的所有属性)。