Перейти к содержимому
Теория · Словари и множества · урок 3 из 11

Обход словаря

В прошлом уроке мы читали табличку цен по одной строке: нашли pear — прочитали 70. Иногда нужна вся табличка сразу. Например, утром продавец переписывает цены в тетрадь: идёт сверху вниз и для каждой строки записывает название и цену.

Цикл for по словарю делает то же самое. Сам по себе он перебирает ключи, то есть названия в строках. Значение можно прочитать по ключу внутри цикла, но словарь умеет отдавать пары целиком. Метод items даёт пары «ключ — значение», и распаковка в заголовке for раскладывает каждую по двум переменным:

Python
prices = {"pear": 70, "plum": 40, "kiwi": 90}
for name in prices:
    print(name, end=" ")  # pear plum kiwi
print()
for name, price in prices.items():
    print(f"{name}={price}", end=" ")  # pear=70 plum=40 kiwi=90

Давайте разберём, что печатает программа:

  1. Первый цикл for name in prices получает только ключи и печатает pear plum kiwi.
  2. print() без аргументов переводит строку, чтобы второй цикл начал печать с новой.
  3. Второй цикл for name, price in prices.items() получает пары и раскладывает каждую по name и price.
  4. f-строка соединяет их через =, и выходит pear=70 plum=40 kiwi=90.

keys, values и items

У словаря три метода для обхода. Каждый отдаёт свою часть таблички.

items

Каждая пара из items — кортеж из ключа и значения. Цикл распаковывает его, как кортежи из enumerate. Это самый частый способ обойти словарь, когда нужны и названия, и цены.

keys

Метод keys отдаёт ключи. Цикл for name in prices.keys() перебирает то же, что for name in prices, поэтому в цикле keys обычно не пишут.

values

Метод values отдаёт значения без ключей. Поэтому sum(prices.values()) складывает цены и даёт 200, общую цену товаров. Проверка "pear" in prices ищет среди ключей, а 70 in prices.values() — среди значений.

Не списки, а особые объекты

Эти три метода возвращают не списки, а особые объекты, которые читают данные прямо из словаря. Что с ними можно делать:

  • обходить в for;
  • передавать в sum, sorted и list;
  • проверять в них элемент через in.

Индексов у них нет: prices.keys()[0] вызывает ошибку TypeError. Нужен список — оберните результат в list: list(prices.keys()) даёт ['pear', 'plum', 'kiwi']. Функция sorted(prices) возвращает список ключей по алфавиту: ['kiwi', 'pear', 'plum'].

Порядок ключей

Словарь помнит порядок вставки, как табличка помнит, в каком порядке продавец писал строки. Обход идёт в том порядке, в котором ключи появились. Тут действуют два правила:

  • запись по существующему ключу меняет значение, но не место ключа;
  • удалённый и заново добавленный ключ встаёт в конец.
Python
d = {"b": 1, "a": 2}
d["c"] = 3
d["b"] = 10
print(list(d))  # ['b', 'a', 'c']
del d["b"]
d["b"] = 4
print(list(d))  # ['a', 'c', 'b']

Разберём по шагам:

  1. Словарь начинается с ключей "b" и "a", затем в конец встаёт "c".
  2. d["b"] = 10 меняет значение, а "b" остаётся первым: ['b', 'a', 'c'].
  3. del d["b"] стирает строку целиком.
  4. d["b"] = 4 пишет её заново, и она встаёт в конец: ['a', 'c', 'b'].

list(d) даёт то же, что list(d.keys()), — список ключей по порядку. Порядок вставки не связан с алфавитом и с величиной значений: список по алфавиту возвращает только sorted. Зато порядок обхода предсказуем: при каждом запуске программа печатает пары в одном и том же порядке.

Этот вопрос часто задают на собеседовании: «Сохраняет ли словарь порядок?» Ответ: да, порядок вставки гарантирован языком начиная с Python 3.7. В более старых версиях порядок обхода не был обещан, поэтому в старых статьях можно встретить обратное.

Частая ошибка

Пары удаляют прямо в цикле по словарю:

Python
stock = {"apple": 3, "pear": 0, "plum": 0}
for name in stock:
    if stock[name] == 0:
        del stock[name]  # ✗
TEXT
RuntimeError: dictionary changed size during iteration

После удаления "pear" цикл пытается взять следующий ключ и останавливается с ошибкой. Пока цикл идёт по словарю, число пар в нём менять нельзя: ни удалять ключи, ни добавлять новые.

Обходите копию ключей — for name in list(stock): — тогда удаление из самого словаря цикл не ломает. Менять значение существующего ключа во время обхода можно: размер словаря от этого не меняется.

Главное

  • for k in d перебирает ключи, а for k, v in d.items() — пары «ключ — значение».
  • keys, values и items возвращают объекты без индексов, а список из них даёт list.
  • Словарь хранит порядок вставки, а по алфавиту ключи сортирует только sorted.
  • Удалять и добавлять ключи в цикле по самому словарю нельзя — обходите list(d).

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом