← Назад к вопросам
В каких случаях кортеж не может быть хешируемым
2.0 Middle🔥 151 комментариев
#Python Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Когда кортеж не хешируем (не hashable)
Основное правило
Кортеж хешируем, только если все его элементы хешируемы.
Если кортеж содержит хотя бы один немутабельный (unhashable) элемент, сам кортеж становится немутабельным.
Примеры немутабельных типов
# ХЕШИРУЕМЫЕ (можно использовать как ключи в dict, элементы set)
hashable_types = [
42, # int
3.14, # float
"hello", # str
True, # bool
None, # NoneType
(1, 2, 3), # tuple из хешируемых элементов
frozenset(), # frozenset
]
# НЕМУТАБЕЛЬНЫЕ (нельзя использовать как ключи)
unhashable_types = [
[1, 2, 3], # list - мутабельный
{'a': 1}, # dict - мутабельный
{1, 2, 3}, # set - мутабельный
(1, [2, 3], 4), # tuple с списком внутри
bytearray(), # bytearray - мутабельный
]
Случай 1: Кортеж содержит список
# ❌ Немутабельный кортеж
tuple_with_list = (1, [2, 3], 4)
# Попытка использовать как ключ словаря
my_dict = {}
# my_dict[tuple_with_list] = "value"
# TypeError: unhashable type: 'list'
# Попытка добавить в set
# my_set = {tuple_with_list}
# TypeError: unhashable type: 'list'
# hash() вернет ошибку
# hash(tuple_with_list)
# TypeError: unhashable type: 'list'
Почему ошибка? Потому что список можно менять, а значит его хеш может измениться.
Случай 2: Кортеж содержит словарь
# ❌ Немутабельный кортеж
tuple_with_dict = ("key", {"name": "Alice"}, 42)
# my_dict[tuple_with_dict] = "value"
# TypeError: unhashable type: 'dict'
# my_set = {tuple_with_dict}
# TypeError: unhashable type: 'dict'
Случай 3: Кортеж содержит set
# ❌ Немутабельный кортеж
tuple_with_set = (1, {2, 3}, 4)
# my_dict[tuple_with_set] = "value"
# TypeError: unhashable type: 'set'
Случай 4: Кортеж содержит другой кортеж с немутабельным элементом
# ❌ Немутабельный кортеж (вложенные кортежи)
inner_tuple = (1, [2, 3]) # Это немутабельный кортеж
outer_tuple = (10, inner_tuple, 20)
# my_dict[outer_tuple] = "value"
# TypeError: unhashable type: 'list'
# Ошибка достает до вложенного кортежа, содержащего список
Случай 5: Кортеж содержит bytearray
# ❌ Немутабельный кортеж
tuple_with_bytearray = (1, bytearray(b"hello"), 2)
# my_dict[tuple_with_bytearray] = "value"
# TypeError: unhashable type: 'bytearray'
Как проверить, хешируем ли кортеж
Способ 1: Попробовать использовать как ключ
def is_hashable(obj):
try:
hash(obj)
return True
except TypeError:
return False
# Примеры
print(is_hashable((1, 2, 3))) # True
print(is_hashable((1, [2, 3], 4))) # False
print(is_hashable(("hello", (1, 2)))) # True
print(is_hashable((1, {"a": 1}))) # False
Способ 2: Использовать collections.abc.Hashable
from collections.abc import Hashable
# ✅ Правильный способ
print(isinstance((1, 2, 3), Hashable)) # True
print(isinstance((1, [2, 3]), Hashable)) # False
print(isinstance([1, 2, 3], Hashable)) # False
print(isinstance({"a": 1}, Hashable)) # False
print(isinstance(frozenset([1, 2]), Hashable)) # True
Практические примеры
Пример 1: Кортеж как ключ словаря
# ✅ Хешируемый кортеж - работает
coordinates = {
(0, 0): "origin",
(1, 2): "point A",
(3, 4): "point B",
}
print(coordinates[(1, 2)]) # point A
# ❌ Немутабельный кортеж - ошибка
# bad_coordinates = {
# (0, [0]): "origin", # TypeError!
# }
Пример 2: Кортеж в set
# ✅ Хешируемый кортеж - работает
unique_coords = {(0, 0), (1, 1), (2, 2)}
print((1, 1) in unique_coords) # True
# ❌ Немутабельный кортеж - ошибка
# bad_set = {(0, 0), (1, [1]), (2, 2)}
# TypeError: unhashable type: 'list'
Пример 3: Функция с кортежом как параметр
# Функция для кеширования (используя dict)
def cache_result(func):
cache = {}
def wrapper(*args):
# args - это кортеж аргументов
if args in cache: # args должен быть хешируем!
return cache[args]
result = func(*args)
cache[args] = result
return result
return wrapper
@cache_result
def expensive_function(x, y):
print(f"Вычисляю {x} + {y}")
return x + y
print(expensive_function(1, 2)) # Вычисляю 1 + 2, результат: 3
print(expensive_function(1, 2)) # Из кэша: 3
# Если бы аргументы содержали список:
# def bad_function(x, items_list): # items_list - список
# # При вызове args будет (1, [1,2,3])
# # Это немутабельный кортеж!
# # TypeError при попытке использовать как ключ
Решения для немутабельных кортежей
Вариант 1: Заменить мутабельные элементы на иммутабельные
# ❌ Плохо - немутабельный кортеж
bad_tuple = (1, [2, 3], 4)
# ✅ Хорошо - заменяем список на кортеж
good_tuple = (1, (2, 3), 4)
print(hash(good_tuple)) # Работает!
# Использовать как ключ
my_dict = {good_tuple: "value"}
print(my_dict[good_tuple]) # value
Вариант 2: Заменить список на frozenset
# Если порядок не важен
bad_tuple = (1, [2, 3, 2], 4)
good_tuple = (1, frozenset([2, 3]), 4)
print(hash(good_tuple)) # Работает!
Вариант 3: Заменить словарь на что-то хешируемое
from types import SimpleNamespace
# ❌ Плохо
bad_tuple = (1, {"name": "Alice", "age": 30}, 2)
# ✅ Хорошо - использовать кортежи вместо dict
good_tuple = (1, (("name", "Alice"), ("age", 30)), 2)
print(hash(good_tuple)) # Работает!
# Или frozenset (если порядок не важен)
good_tuple2 = (1, frozenset([("name", "Alice"), ("age", 30)]), 2)
print(hash(good_tuple2)) # Тоже работает!
Таблица: Какие типы хешируемы
| Тип | Хешируем | Мутабельный | Примеры |
|---|---|---|---|
| int | ✅ | ❌ | 42 |
| float | ✅ | ❌ | 3.14 |
| str | ✅ | ❌ | "hello" |
| tuple | ✅ (если элементы хешируемы) | ❌ | (1, 2, 3) |
| frozenset | ✅ | ❌ | frozenset([1, 2]) |
| list | ❌ | ✅ | [1, 2, 3] |
| dict | ❌ | ✅ | {"a": 1} |
| set | ❌ | ✅ | {1, 2, 3} |
| bytearray | ❌ | ✅ | bytearray(b"hi") |
Вывод
Кортеж немутабельный, если содержит хотя бы один немутабельный элемент (список, словарь, set, bytearray и т.д.).
Проверить, хешируем ли кортеж, можно через:
hash(tuple)— попытка захешироватьisinstance(tuple, collections.abc.Hashable)— проверить тип
Если нужен кортеж как ключ или элемент set, убедись, что все его элементы иммутабельны. Заменяй списки на кортежи, словари на кортежи с парами, sets на frozenset.