Имея на руках список значений, программист на Python всегда может получить из него множество уникальных значений применив к списку функцию set()
. А из списка пар легко получить словарь, применив функцию dict()
. Казалось бы, теперь, имея генераторы списков, мы можем описывать словари и множества столь же декларативно, как и списки.
Однако, в большинстве случаев такое решение не будет оптимальным: в памяти будет создан и сохранён целиком весь промежуточный список. И особенно обидно будет тратить лишнюю память, если мы знаем, что в процессе генерации элементов множества или словаря возникнет много повторяющихся значений или ключей.
Python и здесь приходит нам на помощь, предоставляя в наше пользование генераторы множеств (set comprehensions) и генераторы словарей (dict comprehensions)!
С этими генераторами всё максимально просто: вы всего лишь заменяете в выражении, описывающем генератор списка, квадратные скобки на фигурные! Да, всё настолько просто:
squares = {x * x for x in range(10)}
squares
# {0, 1, 64, 4, 36, 9, 16, 49, 81, 25}
5 * 5 in squares
# True
Вы получаете все те же возможности, которые доступны для генерации списков, но при создании множества Python может ещё и проследить за тем, чтобы в него не попали дубли — и не потратит на них лишнюю память!
Генераторы словарей выглядят очень похоже на генераторы множеств. Разница заключается в том, как описывается элемент словаря: вам нужно сгенерировать не только значение, но и ключ и указать оные через :
так же, как бы вы это сделали при написании литерала словаря.
Пример:
char_positions = {char: pos for pos, char in enumerate("Hello, World!")}
char_positions
# {'H': 0, 'e': 1, 'l': 10, 'o': 8, ',': 5, ' ': 6, 'W': 7, 'r': 9, 'd': 11, '!': 12}
char_positions['o']
# 8
Здесь хотелось бы обратить внимание на то, какое значение имеет ключ 'l'
в этом примере: это 10
. Взглянем на то, какие значения имели char
и pos
во время генерации, и, для простоты, будем смотреть только на позиции символа 'l'
:
[(char, pos) for pos, char in enumerate("Hello, World!") if char == 'l']
# [('l', 2), ('l', 3), ('l', 10)]
Как можно заметить, 'l'
встречается в исходной строке три раза, и в последнем случае как раз в позиции 10
. При генерации словаря используется последнее из значений для каждого из ключей. Как будто словарь был заполнен в подобном цикле:
char_positions = {}
for pos, char in enumerate("Hello, World!"):
char_positions[char] = pos
char_positions
# {'H': 0, 'e': 1, 'l': 10, 'o': 8, ',': 5, ' ': 6, 'W': 7, 'r': 9, 'd': 11, '!': 12}
Заметим, что даже порядок ключей получается тот же самый — это порядок первого появления соответствующего символа в строке. Последующие перезаписи значений этот порядок не меняют — словари в Python запоминают порядок добавления ключей, но не порядок последующих изменений значений.
Но в генераторах множеств в результирующее множество попадают первые уникальные значения. В большинстве случаев это не критично, но помнить об этом стоит.
Вам ответят команда поддержки Хекслета или другие студенты.
Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.
Загляните в раздел «Обсуждение»:
Статья «Ловушки обучения»
Вебинар «Как самостоятельно учиться»
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.
Наши выпускники работают в компаниях:
С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.
Зарегистрируйтесь или войдите в свой аккаунт