← Назад к вопросам

Может ли tuple быть ключом в dictionary?

1.0 Junior🔥 191 комментариев
#Другое

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Tuple как ключ в dictionary

Да, tuple может быть ключом в dictionary, но только если он immutable (неизменяемый). Это одна из ключевых особенностей Python.

Почему tuple может быть ключом

В Python dictionary используют хеширование (hashing) для быстрого поиска ключей. Для этого ключ должен быть хешируемым (hashable).

Тuple — это неизменяемый тип, поэтому его хеш остаётся постоянным:

# ✅ Tuple может быть ключом
coordinates = {}
coordinates[(1, 2)] = "точка A"
coordinates[(3, 4)] = "точка B"
coordinates[(1, 2)] = "обновлённая точка A"

print(coordinates)  # {(1, 2): 'обновлённая точка A', (3, 4): 'точка B'}
print(coordinates[(1, 2)])  # 'обновлённая точка A'

Сравнение с list

# ❌ List НЕ может быть ключом (изменяемый)
cache = {}
cache[[1, 2, 3]] = "значение"
# TypeError: unhashable type: 'list'

# ✅ Tuple может
cache = {}
cache[(1, 2, 3)] = "значение"
print(cache)  # {(1, 2, 3): 'значение'}

Условие для tuple ключей

Тuple может быть ключом ТОЛЬКО если все его элементы тоже хешируемы:

# ✅ Все элементы хешируемы (int, str, tuple)
data = {}
data[(1, "key", (2, 3))] = "good"

# ❌ List внутри tuple - ошибка
data = {}
data[(1, [2, 3])] = "bad"
# TypeError: unhashable type: 'list'

# ❌ Dict внутри tuple - ошибка
data = {}
data[(1, {"a": 2})] = "bad"
# TypeError: unhashable type: 'dict'

Практические примеры

1. Кэш с составным ключом

from functools import lru_cache
from typing import Tuple

# Кэширование с несколькими параметрами
@lru_cache(maxsize=128)
def fibonacci(n: int) -> int:
    if n <= 1:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

# lru_cache работает, потому что int хешируется

# Если нужно кэшировать по нескольким параметрам
class DataCache:
    def __init__(self):
        self.cache = {}
    
    def get(self, user_id: int, date: str) -> any:
        key = (user_id, date)  # Tuple как ключ
        return self.cache.get(key)
    
    def set(self, user_id: int, date: str, value: any) -> None:
        key = (user_id, date)  # Tuple как ключ
        self.cache[key] = value

cache = DataCache()
cache.set(1, "2024-01-01", "some data")
print(cache.get(1, "2024-01-01"))  # 'some data'

2. Таблица переходов (State Machine)

from enum import Enum

class State(Enum):
    IDLE = "idle"
    RUNNING = "running"
    DONE = "done"
    ERROR = "error"

class Event(Enum):
    START = "start"
    FINISH = "finish"
    FAIL = "fail"
    RESET = "reset"

# Таблица переходов
transitions = {
    (State.IDLE, Event.START): State.RUNNING,
    (State.RUNNING, Event.FINISH): State.DONE,
    (State.RUNNING, Event.FAIL): State.ERROR,
    (State.ERROR, Event.RESET): State.IDLE,
    (State.DONE, Event.RESET): State.IDLE,
}

def next_state(current: State, event: Event) -> State:
    return transitions.get((current, event), current)

state = State.IDLE
state = next_state(state, Event.START)
print(state)  # State.RUNNING
state = next_state(state, Event.FINISH)
print(state)  # State.DONE

3. Группировка данных

# Подсчёт по нескольким критериям
data = [
    {"region": "North", "product": "A", "sales": 100},
    {"region": "North", "product": "B", "sales": 150},
    {"region": "South", "product": "A", "sales": 200},
    {"region": "South", "product": "A", "sales": 50},
]

totals = {}
for row in data:
    key = (row["region"], row["product"])
    totals[key] = totals.get(key, 0) + row["sales"]

print(totals)
# {('North', 'A'): 100, ('North', 'B'): 150, ('South', 'A'): 250}

print(totals[("South", "A")])  # 250

4. База данных в памяти

from typing import Dict, Tuple
from dataclasses import dataclass

@dataclass
class User:
    name: str
    age: int

class InMemoryDB:
    def __init__(self):
        self._data: Dict[Tuple[str, int], User] = {}
    
    def insert(self, user_id: int, version: int, user: User) -> None:
        key = (user_id, version)
        self._data[key] = user
    
    def get(self, user_id: int, version: int) -> User:
        return self._data.get((user_id, version))
    
    def delete(self, user_id: int, version: int) -> None:
        key = (user_id, version)
        if key in self._data:
            del self._data[key]

db = InMemoryDB()
db.insert(1, 1, User("Alice", 30))
db.insert(1, 2, User("Alice", 31))

print(db.get(1, 1))  # User(name='Alice', age=30)
print(db.get(1, 2))  # User(name='Alice', age=31)

Хеширование tuple

# Tuple хешируется на основе его элементов
t1 = (1, 2, 3)
t2 = (1, 2, 3)
print(hash(t1))  # Одно и то же значение
print(hash(t2))  # Одно и то же значение
print(hash(t1) == hash(t2))  # True

# Используется для поиска в dict
d = {t1: "value"}
print(d[t2])  # "value" - находит по хешу

# Разные tuple имеют разные хеши
t3 = (1, 2, 4)
print(hash(t3) == hash(t1))  # False

Изменяемый tuple

# Если tuple содержит изменяемый объект
t = (1, 2, [3, 4])  # Список внутри

# Сам tuple неизменяемый (не можешь переассайнить элемент)
# t[0] = 10  # TypeError

# Но список внутри изменяемый!
t[2].append(5)
print(t)  # (1, 2, [3, 4, 5])

# Это может быть проблемой как ключа
d = {}
d[t] = "value"
t[2].append(6)  # Меняем содержимое
print(d[t])  # Всё ещё находит, но логически это плохо

Правило: tuple как ключ

# ✅ Хорошо - простые типы
key1 = (1, 2)
key2 = ("user", "admin")
key3 = (1, "id", (2, 3))

# ❌ Плохо - изменяемые типы внутри
key4 = (1, [2, 3])  # TypeError
key5 = (1, {"a": 2})  # TypeError

# ⚠️ Опасно - мутируемые объекты внутри
class MutableTuple:
    def __init__(self):
        self.lst = [1, 2, 3]
        self.key = (id(self), tuple(self.lst))
        self.cache = {self.key: "value"}
    
    def modify(self):
        # Опасно! Ключ изменился, но находится по старому хешу
        self.lst.append(4)
        self.key = (id(self), tuple(self.lst))

Итого

  • Да, tuple может быть ключом в dictionary
  • Только если все его элементы хешируемы (immutable)
  • Полезно для составных ключей, state machines, кэширования
  • Опасно, если tuple содержит изменяемые объекты
  • Лучше использовать named tuples или dataclasses для сложных ключей
Может ли tuple быть ключом в dictionary? | PrepBro