Как реализовать хорошую хэш-функцию в 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)