Как реализовать хорошую хэш-функцию в python
при реализации класса с несколькими свойствами (как в Примере игрушки ниже), каков наилучший способ обработки хэширования?
думаю, что __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 ответа:
__hash__должен возвращать одинаковое значение для объектов, которые равны. Он также не должен меняться в течение всего срока службы объекта; как правило, вы реализуете его только для неизменяемых объектов.тривиальная реализация будет просто
return 0. Это всегда правильно, но работает плохо.ваше решение, возвращающее хэш кортежа свойств, хорошо. Но учтите, что вам не нужно перечислять все свойства, которые вы сравниваете в
__eq__в кортеже. Если некоторые свойство обычно имеет одинаковое значение для неравных объектов, просто оставьте его. Не делайте вычисление хэша более дорогим, чем это должно быть.Edit: я бы рекомендовал не использовать xor для смешивания хэшей в целом. Когда два разных свойства имеют одинаковое значение, они будут иметь одинаковый хэш, и с xor они будут отменять друг друга. Кортежи используют более сложный расчет для смешивания хэшей, см.
tuplehashнаtupleobject.c.
писать опасно
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не имеет никаких дополнительных атрибутов. Как это сделать, зависит от ситуации (так как нет стандартного способа найти все атрибуты объекта).
документация
object.__hash__(self)def __hash__(self): return hash(self.a) ^ hash(self.b)