Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Типы данных, которые могут быть ключами в словаре (Dict)
В Python словарь может содержать ключи только неизменяемые типы данных (immutable). Это критическое требование для корректной работы хэшировки.
1. Правило: Ключ должен быть хешируемым
Для использования в качестве ключа объект должен быть хешируемым (hashable):
- Иметь метод
__hash__() - Быть неизменяемым (immutable)
- Два равных объекта должны иметь одинаковый хэш
# Проверить, хешируемый ли объект
print(hash(5)) # OK: 5
print(hash("hello")) # OK: 7598361297891167995
print(hash((1, 2))) # OK: 1212408828307725699
# Это не хешируемо
print(hash([1, 2])) # TypeError: unhashable type: 'list'
print(hash({"a": 1})) # TypeError: unhashable type: 'dict'
2. Допустимые типы ключей
Числа (int, float, complex)
data = {}
# int
data[1] = "целое число"
data[100] = "большое число"
# float
data[3.14] = "пи"
data[2.71828] = "е"
# complex
data[1+2j] = "комплексное число"
print(data)
# {1: 'целое число', 100: 'большое число', 3.14: 'пи', 2.71828: 'е', (1+2j): 'комплексное число'}
# Практический пример
temperatures = {}
temperatures[36.6] = "нормальная"
temperatures[37.5] = "повышенная"
temperatures[39.0] = "высокая"
print(temperatures[37.5]) # повышенная
Строки (str)
# Самый частый вариант
user_data = {}
user_data["name"] = "Alice"
user_data["age"] = 25
user_data["email"] = "alice@example.com"
print(user_data["name"]) # Alice
# Многострочные строки тоже ОК
config = {
"database_url": "postgresql://localhost:5432/mydb",
"api_key": "secret_token_12345"
}
# Пустая строка — тоже валидный ключ
empty_key_dict = {"":"значение для пустой строки"}
print(empty_key_dict[""]) # значение для пустой строки
Кортежи (tuple)
Кортеж хешируемый, если все его элементы хешируемы:
# Кортежи как ключи — очень полезно
data = {}
# Координаты
data[(0, 0)] = "начало"
data[(1, 2)] = "точка A"
data[(5, -3)] = "точка B"
print(data[(1, 2)]) # точка A
# Дата
data[(2024, 3, 29)] = "сегодня"
# Вложенные кортежи
data[("user", "alice", "email")] = "alice@example.com"
data[("user", "bob", "age")] = 30
print(data[("user", "alice", "email")]) # alice@example.com
# Практический пример: граф рёбер
graph = {}
graph[("A", "B")] = 5 # Расстояние A→B
graph[("B", "C")] = 3
graph[("A", "C")] = 7
print(graph[("A", "B")]) # 5
Boolean (bool)
settings = {}
settings[True] = "включено"
settings[False] = "отключено"
print(settings[True]) # включено
# Практически всегда используется как конфиг
features = {
True: ["feature_1", "feature_2"],
False: ["disabled_feature"]
}
None
data = {}
data[None] = "нет значения"
data["key"] = "есть значение"
print(data[None]) # нет значения
# Практический пример: обработка отсутствующих данных
results = {
None: "undefined",
"success": "OK",
"error": "ошибка"
}
Bytes
data = {}
# Байты хешируемы
data[b"hello"] = "строка в байтах"
data[b"\x00\x01"] = "бинарные данные"
print(data[b"hello"]) # строка в байтах
# Практический пример
binary_mappings = {
b"\x89PNG": "PNG image",
b"\xFF\xD8\xFF": "JPEG image",
b"GIF8": "GIF image"
}
3. НЕ допустимые типы ключей
Списки (list)
data = {}
data[[1, 2, 3]] = "значение" # TypeError: unhashable type: 'list'
# ПРАВИЛЬНО: использовать кортеж
data[(1, 2, 3)] = "значение" # OK
Словари (dict)
data = {}
data[{"x": 1}] = "значение" # TypeError: unhashable type: 'dict'
# Если нужен составной ключ, используйте кортеж
data[("x", 1)] = "значение" # OK
Множества (set)
data = {}
data[{1, 2, 3}] = "значение" # TypeError: unhashable type: 'set'
# frozenset — замороженное множество, оно хешируемо
data[frozenset([1, 2, 3])] = "значение" # OK
Пользовательские классы (без hash)
class Person:
def __init__(self, name):
self.name = name
person = Person("Alice")
data = {}
data[person] = "информация" # TypeError: unhashable type: 'Person'
4. Проблема с float в качестве ключей
Хотя float хешируемы, есть проблема с точностью:
data = {}
# Работает
data[0.1] = "первое"
data[0.1] = "второе" # Перезапишет
print(data[0.1]) # второе (одна запись)
# ПРОБЛЕМА: неточность float
a = 0.1 + 0.2
b = 0.3
print(a == b) # False! (из-за неточности float)
data[a] = "0.1 + 0.2"
data[b] = "0.3"
print(len(data)) # 3 (два разных ключа вместо одного!)
print(data[0.1 + 0.2]) # 0.1 + 0.2
print(data[0.3]) # 0.3
# РЕШЕНИЕ: использовать Decimal для точных чисел
from decimal import Decimal
precise_data = {}
precise_data[Decimal("0.1")] = "точное"
print(precise_data[Decimal("0.1")]) # точное
5. Хешируемые пользовательские классы
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __hash__(self):
return hash((self.x, self.y))
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __repr__(self):
return f"Point({self.x}, {self.y})"
# Теперь можно использовать как ключ
points = {}
p1 = Point(1, 2)
p2 = Point(1, 2) # Тот же Hash!
p3 = Point(5, 3)
points[p1] = "первая точка"
points[p3] = "третья точка"
print(points[p2]) # первая точка (p1 и p2 равны!)
print(len(points)) # 2
6. frozenset — множество как ключ
# frozenset — неизменяемое множество
data = {}
data[frozenset([1, 2, 3])] = "множество"
data[frozenset(["a", "b"])] = "буквы"
print(data[frozenset([1, 2, 3])]) # множество
# Практический пример: группы пользователей
groups = {
frozenset(["alice", "bob"]): "team_1",
frozenset(["charlie", "diana"]): "team_2"
}
print(groups[frozenset(["alice", "bob"])]) # team_1
7. Сравнительная таблица
| Тип | Хешируемый | Может быть ключом | Примечание |
|---|---|---|---|
| int | Да | Да | Самый быстрый |
| float | Да | Да | Опасно из-за неточности |
| str | Да | Да | Самый частый |
| bool | Да | Да | True/False |
| None | Да | Да | Часто используется |
| bytes | Да | Да | Для бинарных данных |
| tuple | Да (если элементы хешируемы) | Да | Отличный вариант для составных ключей |
| frozenset | Да | Да | Неизменяемое множество |
| list | Нет | Нет | Используйте tuple вместо этого |
| dict | Нет | Нет | Используйте tuple вместо этого |
| set | Нет | Нет | Используйте frozenset вместо этого |
8. Практические примеры
# Граф — словарь словарей
graph = {
"A": {"B": 5, "C": 2},
"B": {"A": 5, "C": 1},
"C": {"A": 2, "B": 1}
}
# Кэш с кортежами как ключи
cache = {
("user", 1, "profile"): {"name": "Alice"},
("user", 2, "profile"): {"name": "Bob"}
}
# Счётчик уникальных пар
pairs = {}
pairs[("apple", "orange")] += 1
pairs[("apple", "banana")] += 1
# Группировка по типам
data_by_type = {}
data_by_type[int] = [1, 2, 3]
data_by_type[str] = ["a", "b", "c"]
Выводы
Ключи словаря должны быть хешируемыми:
Допустимые типы:
- int, float, complex
- str
- tuple (если содержит только хешируемые объекты)
- bool, None
- bytes
- frozenset
Недопустимые типы:
- list (используйте tuple)
- dict (используйте tuple)
- set (используйте frozenset)
Лучшие практики:
- Для простых ключей: используйте int или str
- Для составных ключей: используйте tuple
- Для логических флагов: используйте bool или None
- Для множеств: используйте frozenset
- Избегайте float как ключи из-за неточности
- Для пользовательских объектов: реализуйте
__hash__()и__eq__()