TL; DR:
См. Глоссарий : hash()используется как ярлык для сравнения объектов, объект считается хешируемым, если его можно сравнить с другими объектами. вот почему мы используем hash(). Он также используется для доступа dictи setэлементов, реализованных в виде изменяемого размера хэш - таблицы в CPython .
Технические соображения
- обычно сравнение объектов (которое может включать несколько уровней рекурсии) обходится дорого.
- предпочтительно, чтобы эта
hash()функция была на порядок (или несколько) дешевле.
- сравнивать два хэша проще, чем сравнивать два объекта, здесь и находится ярлык.
Если вы читали о том, как реализованы словари , они используют хэш-таблицы, а это означает, что получение ключа из объекта является краеугольным камнем для получения объектов в словарях в O(1). Однако это очень зависит от вашей хэш-функции, чтобы быть устойчивой к столкновениям . Худший случай для получения элемента в словаре фактически O(n).
При этом изменяемые объекты обычно не хешируются. Свойство hashable означает, что вы можете использовать объект в качестве ключа. Если хеш-значение используется в качестве ключа и содержимое того же объекта изменяется, то что должна возвращать хеш-функция? Это тот же ключ или другой? Это зависит от того, как вы определяете свою хеш-функцию.
Учимся на примере:
Представьте, что у нас есть этот класс:
>>> class Person(object):
... def __init__(self, name, ssn, address):
... self.name = name
... self.ssn = ssn
... self.address = address
... def __hash__(self):
... return hash(self.ssn)
... def __eq__(self, other):
... return self.ssn == other.ssn
...
Обратите внимание: все это основано на предположении, что SSN никогда не меняется для человека (даже не знаю, где на самом деле проверить этот факт из авторитетного источника).
И у нас есть Боб:
>>> bob = Person('bob', '1111-222-333', None)
Боб идет к судье, чтобы изменить свое имя:
>>> jim = Person('jim bo', '1111-222-333', 'sf bay area')
Вот что мы знаем:
>>> bob == jim
True
Но это два разных объекта с разной выделенной памятью, как две разные записи одного и того же человека:
>>> bob is jim
False
Теперь подошло время использовать hash ():
>>> dmv_appointments = {}
>>> dmv_appointments[bob] = 'tomorrow'
Угадай, что:
>>> dmv_appointments[jim]
'tomorrow'
Из двух разных записей вы можете получить доступ к одной и той же информации. А теперь попробуйте это:
>>> dmv_appointments[hash(jim)]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 9, in __eq__
AttributeError: 'int' object has no attribute 'ssn'
>>> hash(jim) == hash(hash(jim))
True
Что только что произошло? Это столкновение. Поскольку hash(jim) == hash(hash(jim))оба являются целыми числами, нам нужно сравнить входные данные __getitem__со всеми элементами, которые конфликтуют. У встроенной функции intнет ssnатрибута, поэтому она отключается.
>>> del Person.__eq__
>>> dmv_appointments[bob]
'tomorrow'
>>> dmv_appointments[jim]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: <__main__.Person object at 0x7f611bd37110>
В этом последнем примере я показываю, что даже при столкновении выполняется сравнение, объекты больше не равны, что означает, что он успешно вызывает KeyError.