如何在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.a
和self.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__函数[重复]的主要内容,如果未能解决你的问题,请参考以下文章