Словари

Теория

Словарь (dict) хранит пары ключ → значение: поле товара → его значение, id товара → товар, статус → число заказов. Значение достают не по номеру позиции, как в списке, а по ключу — и это очень быстро, сколько бы пар ни было в словаре. В backend словари повсюду: строка из базы, тело JSON-запроса, настройки, кэш — всё это словари или очень похожие на них структуры.

Ключи в словаре уникальны: запись по существующему ключу заменяет старое значение. Ключом может быть строка, число, кортеж — значение, которое не меняется (точное правило — в курсе «Python под капотом»); список ключом быть не может. Словарь изменяемый и с Python 3.7 сохраняет порядок добавления ключей. Чтобы обращение к отсутствующему ключу не роняло программу, есть метод get.

Наглядно

Таблица 1. Основные операции со словарём product = {"name": "Мышь", "price": 1499, "stock": 12}.

Операция Пример Результат
значение по ключу product["price"] 1499; нет ключа — KeyError
безопасное чтение product.get("color"), product.get("color", "не указан") None, "не указан"
добавить или заменить product["stock"] = 11 ключ есть — замена, нет — добавление
удалить product.pop("stock"), del product["stock"] pop возвращает значение
проверить ключ "price" in product True — in проверяет ключи
число пар len(product) 3
ключи, значения, пары .keys(), .values(), .items() для перебора в цикле
объединить product.update({"color": "black"}) добавит/заменит ключи из другого словаря

На практике

Задача: описать товар словарём, собрать каталог {id: товар} и посчитать заказы по статусам.

Шаг 1. Создаём словарь

Товар — набор полей с именами, как строка таблицы products:

python
product = {"id": 42, "name": "Беспроводная мышь", "price": 1499, "stock": 12}
print(product["name"], product["price"])
print(len(product))

Результат:

вывод
Беспроводная мышь 1499
4

Шаг 2. Читаем безопасно

У этого товара нет поля color. Квадратные скобки падают, get — нет:

python
print(product.get("color"))
print(product.get("color", "не указан"))
product["color"]

Результат:

вывод
None
не указан
KeyError: 'color'

get возвращает None или значение по умолчанию и не добавляет ключ в словарь.

Шаг 3. Добавляем и меняем значения

Запись по ключу заменяет значение, если ключ есть, и добавляет пару, если нет:

python
product["stock"] = 11
product["color"] = "black"
print(product)

Результат:

вывод
{'id': 42, 'name': 'Беспроводная мышь', 'price': 1499, 'stock': 11, 'color': 'black'}

Новый ключ встал в конец — словарь помнит порядок добавления.

Шаг 4. Удаляем значения

pop удаляет ключ и возвращает значение; второй аргумент — значение на случай, если ключа нет:

python
color = product.pop("color")
discount = product.pop("discount", 0)
print(color, discount)
print("color" in product)

Результат:

вывод
black 0
False

Шаг 5. Проверяем наличие ключа

in для словаря проверяет ключи, а не значения:

python
print("stock" in product, 11 in product, 11 in product.values())

Результат:

вывод
True False True

Шаг 6. Обходим словарь

Цикл по словарю перебирает ключи; пары удобно брать через items() с распаковкой:

python
for key, value in product.items():
    print(f"{key}: {value}")

Результат:

вывод
id: 42
name: Беспроводная мышь
price: 1499
stock: 11

Шаг 7. Строим каталог

Каталог — словарь, где ключ — id товара, а значение — сам товар (тоже словарь):

python
catalog = {
    42: {"name": "Беспроводная мышь", "price": 1499},
    7: {"name": "Механическая клавиатура", "price": 5990},
}
print(catalog[7]["name"])
print(catalog.get(13))

Результат:

вывод
Механическая клавиатура
None

Поиск по id в словаре не перебирает товары — он сразу находит нужный, даже если товаров миллион.

Шаг 8. Считаем по ключам

Сколько заказов в каждом статусе. Для нового ключа берём 0 через get:

python
statuses = ["paid", "new", "paid", "cancelled", "paid"]
counts = {}
for status in statuses:
    counts[status] = counts.get(status, 0) + 1
print(counts)

Результат:

вывод
{'paid': 3, 'new': 1, 'cancelled': 1}

Шаг 9. Не меняем размер словаря при обходе

Удалить отменённые статусы прямо в цикле по словарю нельзя:

python
for status in counts:
    if status == "cancelled":
        del counts[status]

Результат:

вывод
RuntimeError: dictionary changed size during iteration

Перебирайте копию ключей — for status in list(counts): — или соберите новый словарь.

Частые ошибки

  • d[key] для необязательного поля. Нет ключа — KeyError. Для полей, которых может не быть, — d.get(key, значение_по_умолчанию).
  • counts[key] += 1 для нового ключа. Ключа ещё нет — KeyError. Используйте counts.get(key, 0) + 1.
  • Ключ не того типа. "42" и 42 — разные ключи: catalog["42"] не найдёт товар с ключом 42. Данные из URL и форм — строки, их приводят к типу ключей.
  • in по значениям. 11 in product проверяет ключи. Для значений — 11 in product.values().
  • Изменение размера при обходе. Добавление или удаление ключей в цикле по словарю — RuntimeError.

Итог

Словарь

  • Пары ключ → значение; поиск по ключу быстрый при любом размере.
  • Ключи уникальны, неизменяемого типа (str, int, tuple); порядок добавления сохраняется.

Чтение и запись

  • d[k] — значение или KeyError; d.get(k, default) — без ошибки и без добавления ключа.
  • d[k] = v — добавить или заменить; d.pop(k, default), del d[k] — удалить.
  • k in d — проверка ключа.

Обход и подсчёт

  • for k in d, for k, v in d.items(), d.values().
  • Подсчёт: counts[k] = counts.get(k, 0) + 1.
  • Не добавлять и не удалять ключи во время обхода.

Вопросы на собеседовании

Чем d[key] отличается от d.get(key)?

d[key] выбрасывает KeyError, если ключа нет. d.get(key) возвращает None (или переданное значение по умолчанию). Ни тот, ни другой не добавляют ключ в словарь.

Сохраняет ли словарь порядок элементов?

Да, с Python 3.7 порядок добавления ключей гарантирован спецификацией языка. Повторная запись по существующему ключу место не меняет. OrderedDict нужен только ради специфичных методов вроде move_to_end.

Что может быть ключом словаря?

Хешируемый объект: числа, строки, None, кортежи из хешируемых значений, frozenset. Списки, словари и множества — нет: они изменяемые, их хеш не постоянен.

Почему поиск по ключу в словаре быстрее, чем поиск в списке?

Словарь — хеш-таблица: по хешу ключа сразу вычисляется место, где лежит значение, — в среднем O(1). Список при поиске значения перебирает элементы по очереди — O(n).

Глубже

Как словарь находит ключ, что такое хеш и почему список не может быть ключом — в курсе «Python под капотом», темы «Hashable и неизменяемость» и «Как устроен dict».

Ошибка в тексте

Сообщить об ошибке могут зарегистрированные пользователи — так в сообщениях меньше спама.

Войти, чтобы сообщить