В чем разница между неизменяемыми и хэшируемыми типами данных

«В чем разница между неизменяемыми и хэшируемыми типами данных» — вопрос из категории Python, который задают на 24% собеседований AQA / Automation. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Неизменяемость (immutability) и хэшируемость (hashability) — это разные, но часто связанные свойства объектов в Python.

  • Неизменяемый объект — это объект, состояние которого нельзя изменить после создания. При любой "операции изменения" создается новый объект. Примеры: int, float, str, tuple, frozenset.
  • Хэшируемый объект — это объект, который имеет хэш-значение (целое число), неизменяемое на протяжении его жизни, и может корректно сравниваться с другими объектами (имеет метод __eq__). Хэшируемые объекты могут быть ключами в словарях (dict) и элементами множеств (set).

Ключевая связь и различие:

  1. Почти все встроенные неизменяемые типы хэшируемы.
  2. Изменяемые типы (списки list, словари dict, множества set) — нехэшируемы.
  3. Хэшируемость не гарантирует неизменяемость. Можно создать пользовательский изменяемый класс и определить для него метод __hash__, но это считается антипаттерном, так как изменение объекта после помещения в словарь нарушит структуру данных.

Примеры:

# 1. Неизменяемый и хэшируемый объект (строка)
my_string = "hello"
my_dict = {my_string: "value"}  # Допустимо, строка хэшируема
print(hash(my_string))  # Выведет хэш-значение

# 2. Изменяемый и нехэшируемый объект (список)
my_list = [1, 2, 3]
try:
    my_dict = {my_list: "value"}  # Вызовет TypeError
except TypeError as e:
    print(e)  # unhashable type: 'list'

# 3. Кортеж (tuple) хэшируем, только если все его элементы хэшируемы.
tuple_hashable = (1, "two", 3.0)
print(hash(tuple_hashable))  # Работает

tuple_unhashable = (1, [2, 3])  # Содержит изменяемый список
try:
    hash(tuple_unhashable)     # Вызовет TypeError
except TypeError as e:
    print(e)  # unhashable type: 'list'

Для тестирования важно понимать эти различия, например, при использовании фикстур pytest или данных в параметризованных тестах, где в качестве ключей параметризации могут использоваться только хэшируемые значения.