Перейти к содержимому
Теория · Включения и генераторные выражения · урок 3 из 8

Включения для словарей и множеств

В прошлом уроке конвейер складывал плитки в ящик-список. Теперь поменяем то, что стоит в конце ленты. Пусть пресс выбивает на каждой плитке ярлык и число под ним, а плитки попадают в картотеку, где число находят по ярлыку. Это словарь, и для него есть своё включение — в фигурных скобках.

Вы уже строили словарь циклом: создавали пустой словарь и в каждой итерации записывали значение по ключу. Сравните такой цикл с включением:

Python
words = ["apple", "fig", "kiwi"]
lengths = {}
for w in words:
    lengths[w] = len(w)
print(lengths)  # {'apple': 5, 'fig': 3, 'kiwi': 4}

lengths = {w: len(w) for w in words}
print(lengths)  # {'apple': 5, 'fig': 3, 'kiwi': 4}

Давайте разберём обе версии:

  1. Цикл создаёт пустой словарь lengths и для каждого слова выполняет lengths[w] = len(w).
  2. Включение {w: len(w) for w in words} делает те же записи само.
  3. Обе версии печатают {'apple': 5, 'fig': 3, 'kiwi': 4}.

Во включении словаря перед for стоит пара «ключ: значение» через двоеточие. Каждая итерация добавляет в словарь одну пару, как присваивание lengths[w] = len(w) в цикле. Порядок работы тот же, что в списковом включении. Сначала работает for, затем фильтр if, если он есть, и последней — пара слева.

Три примера в фигурных скобках

Фигурные скобки умеют больше, чем перекладывать слова в словарь. В этом примере три включения:

Python
words = ["ox", "to", "sun"]
print(sorted({len(w) for w in words}))  # [2, 3]
prices = {"pear": 70, "plum": 40}
print({k: v * 2 for k, v in prices.items()})  # {'pear': 140, 'plum': 80}
print({k: v for k, v in prices.items() if v > 50})  # {'pear': 70}

Множество

Если перед for стоит одно выражение без двоеточия, получается включение множества. На конвейере это ящик, который не принимает вторую плитку с тем же числом.

Длины слов здесь 2, 2 и 3. Множество хранит каждое значение один раз, поэтому две длины 2 дали один элемент. Порядка у множества нет, и для печати его оборачивают в sorted.

Новые значения из items()

Заголовок for k, v in prices.items() перебирает пары словаря. Распаковка раскладывает каждую пару по двум именам, как в обычном цикле.

Включение {k: v * 2 for k, v in prices.items()} оставляет ключи прежними, а цены удваивает. Получается новый словарь, а prices не меняется.

Фильтр по значению

Фильтр if после for работает так же, как в списковом включении. В словаре {k: v for k, v in prices.items() if v > 50} осталась только груша: она дороже 50, а слива дешевле.

Пары из zip

Ключи и значения часто лежат в двух списках одинаковой длины. Функция zip соединяет их в пары, а включение раскладывает каждую пару на ключ и значение.

Возьмём names = ["ann", "bob"] и scores = [90, 75]. Запись {n: s for n, s in zip(names, scores)} даёт {'ann': 90, 'bob': 75}. Если длины списков разные, zip остановится на коротком, и лишние ключи в словарь не попадут.

Повтор ключа

Здесь аналогия с картотекой перестаёт работать. В настоящей картотеке две карточки с одним ярлыком могут стоять рядом. В словаре ключ встречается один раз, и вторая пара с тем же ключом не добавляется, а заменяет значение.

Ошибки при этом нет. Как и запись d[k] = v в цикле, более поздняя пара заменяет значение, а ключ остаётся на прежнем месте:

Python
names = ["anna", "bob", "alex"]
by_letter = {n[0]: n for n in names}
print(by_letter)  # {'a': 'alex', 'b': 'bob'}
print(len(by_letter))  # 2

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

  1. Первая итерация записывает "anna" под ключом "a".
  2. Вторая добавляет "bob" под ключом "b".
  3. Третья даёт тот же ключ "a" и заменяет "anna" на "alex".

Поэтому в словаре две пары, а не три, и ключ "a" стоит первым, где его записала первая итерация. Этот вопрос часто задают на собеседовании: сколько пар окажется в словаре, если ключи повторяются.

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

Ключ и значение разделили запятой вместо двоеточия:

Python
words = ["apple", "fig", "kiwi"]
lengths = {w, len(w) for w in words}  # ✗
TEXT
SyntaxError: did you forget parentheses around the comprehension target?

Перед for в фигурных скобках Python ждёт пару через двоеточие или одно выражение, а w, len(w) — два выражения. Поэтому файл не запускается. Скобки из подсказки дали бы множество кортежей {(w, len(w)) for w in words}, а не словарь. Словарь даёт двоеточие: {w: len(w) for w in words}.

Главное

  • Включение словаря {w: len(w) for w in words} ставит перед for пару через двоеточие.
  • Без двоеточия фигурные скобки дают множество, и повторы в нём пропадают.
  • При повторе ключа более поздняя пара заменяет значение, а ключ остаётся на первом месте.
  • Запятая вместо двоеточия между ключом и значением — SyntaxError.

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

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

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