实现具有多个属性的类时(如下面的玩具示例中),处理散列的最佳方法是什么?
我想__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上读到元组是可以清洗的,所以我想知道上面的例子是否合情合理。是吗?
答案 0 :(得分:68)
__hash__
应该为相同的对象返回相同的值。它也不应该在对象的生命周期内改变;通常你只为不可变对象实现它。
一个简单的实现只是return 0
。这总是正确的,但表现不佳。
你的解决方案,返回一个属性元组的哈希是好的。但请注意,您不需要列出元组中__eq__
中比较的所有属性。如果某些属性对于不等对象通常具有相同的值,则只需将其保留。不要使哈希计算比它需要的更昂贵。
编辑:我建议不要使用xor来混合哈希。当两个不同的属性具有相同的值时,它们将具有相同的散列,并且对于xor,这些将取消彼此。元组使用更复杂的计算来混合哈希,请参阅tupleobject.c
中的tuplehash
。
答案 1 :(得分:12)
写
是危险的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
不是没有任何其他属性。你如何做到这取决于具体情况(因为没有标准的方法来查找对象的所有属性)。
答案 2 :(得分:11)
唯一需要的属性是比较相等的对象具有相同的哈希值;建议以某种方式将对象组件的哈希值混合在一起(例如使用exclusive或),这些哈希值也是对象比较中的一部分。
def __hash__(self):
return hash(self.a) ^ hash(self.b)