Джон Милликин предложил решение, подобное этому:
class A(object):
def __init__(self, a, b, c):
self._a = a
self._b = b
self._c = c
def __eq__(self, othr):
return (isinstance(othr, type(self))
and (self._a, self._b, self._c) ==
(othr._a, othr._b, othr._c))
def __hash__(self):
return hash((self._a, self._b, self._c))
Проблема с этим решением заключается в том, что hash(A(a, b, c)) == hash((a, b, c))
. Другими словами, хэш сталкивается с хэшем кортежа его ключевых членов. Может быть, это не имеет большого значения на практике?
Обновление: документы Python теперь рекомендуют использовать кортеж, как в примере выше. Обратите внимание, что в документации говорится
Единственным обязательным свойством является то, что объекты, которые сравниваются равными, имеют одинаковое значение хеш
Обратите внимание, что обратное неверно. Объекты, которые не сравниваются равными, могут иметь одинаковое значение хеш-функции. Такое столкновение хэшей не приведет к тому, что один объект заменит другой при использовании в качестве ключа dict или элемента set, если объекты также не будут сравниваться .
Устаревшее / плохое решение
Документация Python__hash__
предлагает объединить хэши подкомпонентов, используя что-то вроде XOR , что дает нам это:
class B(object):
def __init__(self, a, b, c):
self._a = a
self._b = b
self._c = c
def __eq__(self, othr):
if isinstance(othr, type(self)):
return ((self._a, self._b, self._c) ==
(othr._a, othr._b, othr._c))
return NotImplemented
def __hash__(self):
return (hash(self._a) ^ hash(self._b) ^ hash(self._c) ^
hash((self._a, self._b, self._c)))
Обновление: как указывает Blckknght, изменение порядка a, b и c может вызвать проблемы. Я добавил дополнительный, ^ hash((self._a, self._b, self._c))
чтобы захватить порядок значений хэширования. Этот финал ^ hash(...)
может быть удален, если объединяемые значения нельзя переставить (например, если они имеют разные типы и, следовательно, значение _a
никогда не будет присвоено _b
или _c
, и т. Д.).
__key
функции, это примерно так же быстро, как и любой хэш. Конечно, если известно, что атрибуты являются целыми числами, и их не так уж много, я полагаю, что вы могли бы потенциально работать немного быстрее с некоторыми хэшированными в домашних условиях хэшами, но, скорее всего, они не будут так хорошо распределены.hash((self.attr_a, self.attr_b, self.attr_c))
это будет на удивление быстро (и правильно ), поскольку создание маленькихtuple
s специально оптимизировано, и это продвигает работу по получению и объединению хешей для встроенных Си, что обычно быстрее, чем код уровня Python.