Джон Міллікін запропонував подібне рішення:
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 тепер рекомендують використовувати кортеж, як у наведеному вище прикладі. Зауважте, що в документації зазначено
Єдиною необхідною властивістю є те, що об'єкти, які порівнюють рівні, мають однакове хеш-значення
Зауважте, що навпаки не вірно. Об'єкти, які не порівнюють рівних, можуть мати однакове хеш-значення. Таке зіткнення хешу не призведе до того, що один об'єкт замінить інший при використанні в якості ключа дікта або набору елементів до тих пір, поки об'єкти не порівнюються рівними .
Застаріле / погане рішення
Документація 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 спеціально оптимізовано, і це підштовхує роботу над отриманням та комбінуванням хешей до вбудованих C, що, як правило, швидше, ніж код рівня Python.