Как реализовать хорошую функцию __hash__ в python - PullRequest
88 голосов
/ 23 октября 2010

При реализации класса с несколькими свойствами (как в примере с игрушкой ниже), как лучше всего обрабатывать хеширование?

Я полагаю, что __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))

Я прочитал на этот вопрос , что кортежи можно хэшировать, поэтому мне было интересно, было ли что-то вроде приведенного выше примера разумно Это 1010 *

Ответы [ 3 ]

68 голосов
/ 23 октября 2010

__hash__ должно возвращать одинаковое значение для объектов, которые равны. Это также не должно измениться в течение жизни объекта; как правило, вы реализуете его только для неизменяемых объектов.

Тривиальная реализация будет просто return 0. Это всегда правильно, но плохо работает.

Ваше решение, возвращающее хэш набора свойств, является хорошим. Но обратите внимание, что вам не нужно перечислять все свойства, которые вы сравниваете в __eq__ в кортеже. Если какое-либо свойство обычно имеет одинаковое значение для неравных объектов, просто не указывайте его. Не делайте хэш-вычисление дороже, чем нужно.

Редактировать: я бы рекомендовал не использовать xor для смешивания хешей вообще. Когда два разных свойства имеют одно и то же значение, они будут иметь одинаковый хеш, и с помощью xor они будут отменять друг друга. Кортежи используют более сложные вычисления для смешивания хэшей, см. tuplehash в tupleobject.c.

11 голосов
/ 20 сентября 2012

Писать опасно

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 не имеет никаких дополнительные атрибуты. Как это сделать, зависит от ситуации (поскольку нет стандартного способа найти все атрибуты объекта).

11 голосов
/ 23 октября 2010

Документация для object.__hash__(self)

Единственное обязательное свойство состоит в том, что сравниваемые объекты имеют одинаковое значение хеш-функции;Рекомендуется каким-то образом смешивать (например, используя exclusive или) хеш-значения для компонентов объекта, которые также играют роль в сравнении объектов.

def __hash__(self):
    return hash(self.a) ^ hash(self.b)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...