← Назад к вопросам
Какая связь между hash-функцией и изменяемостью/неизменяемостью типов?
3.0 Senior🔥 61 комментариев
#Python Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Hash-функции и изменяемость типов
Основной принцип: Объект может использоваться как ключ словаря или элемент множества только если его хеш не меняется. Это значит, что только неизменяемые (immutable) типы могут быть hashable.
Неизменяемые типы — hashable
# int, float, str, tuple - неизменяемые, hashable
print(hash(42)) # 42
print(hash(3.14)) # 3.14
print(hash("hello")) # 8946734968093915261
print(hash((1, 2, 3))) # -8027176449867935021
# Можно использовать как ключи в словаре
my_dict = {
42: "answer",
"name": "John",
(1, 2): "coordinates",
}
print(my_dict[42]) # "answer"
print(my_dict[(1, 2)]) # "coordinates"
# Можно добавлять в set
unique_values = {1, 2, 3, "hello", (1, 2)}
print(unique_values) # {1, 2, 3, "hello", (1, 2)}
Изменяемые типы — NOT hashable
# list, dict, set - изменяемые, НЕ hashable
print(hash([1, 2, 3])) # TypeError: unhashable type: "list"
print(hash({"key": "value"})) # TypeError: unhashable type: "dict"
print(hash({1, 2, 3})) # TypeError: unhashable type: "set"
# Нельзя использовать как ключи
my_dict = {
[1, 2]: "coordinates" # TypeError: unhashable type: "list"
}
# Нельзя добавлять в set
unique_lists = {[1, 2], [3, 4]} # TypeError: unhashable type: "list"
Почему так? Логика изменяемости
# Неизменяемый объект - его хеш остаётся одинаковым
name = "Alice"
name_hash_1 = hash(name)
# Строка не может измениться, хеш не меняется
name_hash_2 = hash(name)
print(name_hash_1 == name_hash_2) # True - всегда True
# Если бы строка была изменяемой и мы могли бы сделать:
# name[0] = "B" # Измени первый символ
# name_hash_2 = hash(name) # Хеш изменится!
# Словарь потеряет ключ в своей хеш-таблице!
Пример проблемы с изменяемостью
# Представим, что списки можно было бы использовать как ключи
my_dict = {}
key = [1, 2, 3]
my_dict[key] = "value"
# Словарь добавил ключ в хеш-таблицу
# Потом мы изменили список
key.append(4)
# Хеш изменился: hash([1, 2, 3, 4]) != hash([1, 2, 3])
# Но ключ в словаре остался в СТАРОЙ позиции хеш-таблицы
# Словарь теперь не найдёт свой же ключ!
# my_dict[key] # KeyError!
Tuple - особый случай
# Кортежи неизменяемы И hashable
coord = (10, 20)
print(hash(coord)) # hashable
my_dict = {coord: "location"} # Работает
my_set = {coord, (5, 10)} # Работает
# НО! Если tuple содержит изменяемый объект, он НЕ hashable
mixed_tuple = (1, [2, 3]) # list внутри tuple
print(hash(mixed_tuple)) # TypeError: unhashable type: "list"
# Правило: tuple hashable только если все его элементы hashable
good_tuple = (1, 2, "hello") # Все элементы hashable
print(hash(good_tuple)) # Работает
bad_tuple = (1, 2, {"a": 1}) # dict внутри - НЕ hashable
print(hash(bad_tuple)) # TypeError
Кастомные классы - важное правило
class Person:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
def __eq__(self, other):
return self.name == other.name and self.age == other.age
# ВАЖНО! После __eq__ Python считает класс unhashable!
# Нужно явно определить __hash__
person1 = Person("Alice", 30)
print(hash(person1)) # TypeError: unhashable type
Как правильно сделать класс hashable
from typing import Any
class Person:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
def __eq__(self, other: Any) -> bool:
if not isinstance(other, Person):
return False
return self.name == other.name and self.age == other.age
def __hash__(self) -> int:
# Хеш должен быть основан на неизменяемых полях
return hash((self.name, self.age))
person1 = Person("Alice", 30)
person2 = Person("Alice", 30)
print(person1 == person2) # True
print(hash(person1) == hash(person2)) # True - важно!
my_dict = {person1: "employee"}
print(my_dict[person2]) # "employee" - находит по равенству
Правило для immutable классов
from dataclasses import dataclass
# frozen=True делает класс неизменяемым и автоматически hashable
@dataclass(frozen=True)
class Point:
x: int
y: int
p1 = Point(1, 2)
p2 = Point(1, 2)
print(hash(p1) == hash(p2)) # True
print(p1 == p2) # True
my_set = {p1, p2}
print(my_set) # {Point(x=1, y=2)} - дубликат удален
Ключевые выводы
- Hashable объекты = неизменяемые (int, str, tuple, frozenset)
- Unhashable объекты = изменяемые (list, dict, set, обычные классы)
- Почему? При изменении объекта его хеш меняется, и словарь/set теряют возможность найти ключ
- Правило: Если переопределяешь
__eq__, определи и__hash__ - Лучшая практика: Используй
@dataclass(frozen=True)для hashable иммутабельных классов