Хэш объектов — это целочисленное числовое представление, которое получается с помощью метода dunder __hash__
. Понимание этой концепции помогает понять, как работают структуры данных Python, поскольку хэш объектов используется внутри.
Программисты Python часто сталкиваются с концепцией объектного хэша, когда пытаются сохранить объект без хэша в структуре данных языка. Например:
from dataclasses import dataclass
@dataclass
class Pessoa:
nome: str
cpf: str
p = Pessoa(nome="Ítalo Epifânio", cpf="1010101010")
pessoas = set()
pessoas.add(p)
Приведенный выше код определяет класс person и объект p
типа person. При попытке добавить человека в набор person
возникает следующая ошибка:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'Pessoa'
Это происходит потому, что структура данных Python «set» использует хэш в своих внутренних таблицах для быстрого поиска значения объекта. Поскольку наш объект не имеет хэша, возникает вышеуказанная ошибка.
Для добавления хэша к объекту мы реализуем функции __hash__
и __eq__
одновременно (при использовании Python 2 необходимо также добавить функцию __ne__
). В следующем примере мы модифицируем наш предыдущий класс, чтобы он приобрел это свойство.
@dataclass
class Pessoa:
nome: str
cpf: str
def __hash__(self):
return hash(self.cpf)
def __eq__(self, other):
mesma_classe = self.__class__ == other.__class__
mesmo_cpf = self.cpf == other.cpf
return mesma_classe and mesmo_cpf
p = Pessoa(nome="Ítalo Epifânio", cpf="101.010.101-01")
pessoas = set()
pessoas.add(p)
Теперь код может быть выполнен без предыдущей ошибки. Если мы выполним команду print(people)
, то увидим, что массив содержит следующее значение:
{Pessoa(nome='Ítalo Epifânio', cpf=1010101010)}
Объект считается хэшируемым, если хэш-значение никогда не изменяется в течение его жизни.
— Документы по Python
Просто реализация вышеуказанных методов не гарантирует, что объект является хэшируемым. Вы должны убедиться, что хэш-значение этого объекта никогда не изменяется, поскольку это может привести, например, к неожиданному поведению:
p = Pessoa(nome="Ítalo Epifânio", cpf="101.010.101-01")
pessoas = set()
pessoas.add(p)
print(p in pessoas) # retorna True
p.cpf = "999.999.999-99"
print(p in pessoas) # retorna False
print(pessoas) # retorna {Pessoa(nome='Ítalo Epifânio', cpf='999.999.999-99')}
При изменении cpf объекта p
обратите внимание, что объект больше не найден в структуре данных set (второй вызов print(p in people)
возвращает false), а при перечислении значений набора people
вы заметили, что там все еще есть значение.
В итоге: вы не можете основывать хэш объектов на изменяемых значениях. Если атрибут объекта может быть изменен во время его жизненного цикла, может произойти неожиданное поведение.