На этом шаге мы рассмотрим использование включений для коллекций различных типов.
Одним из часто используемых функциональных средств языка Python - включения в список. На первый взгляд эта конструкция может показаться немного загадочной, но когда вы разложите ее по полочкам, она окажется очень простой.
Ключ к пониманию конструкций включения в список состоит в том, что они попросту являются циклами с обходом коллекции, выраженными при помощи более сжатого и компактного синтаксиса.
Такие синтаксические конструкции, или синтаксический сахар, - небольшая краткая форма для часто используемой функциональности, которая делает нашу программистскую питоновскую жизнь легче. В качестве примера возьмем приведенное ниже включение в список:
>>> squares = [x * x for x in range(10)]
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Если бы вы хотели построить тот же самый список, использовав обыкновенный цикл for, то вы, вероятно, написали бы что-то типа этого:
>>> squares = [] >>> for x in range(10): squares.append(x * x)
Довольно-таки прямолинейный цикл, не правда ли? Если вы вернетесь и сопоставите пример с включением в список и версию с циклом for, то заметите общие черты, и в конечном счете у вас появятся некоторые шаблоны. Обобщив здесь часть общей структуры, вы в итоге придете к шаблону, похожему на следующий:
values = [expression for item in collection]
Приведенный выше "шаблон" включения в список эквивалентен представленному ниже обыкновенному циклу for:
values = [] for item in collection: values.append(expression)
Здесь мы сначала настраиваем новый экземпляр списка list, который получит выходные значения. Затем мы выполняем обход всех значений в контейнере, преобразовывая каждый из них при помощи произвольного выражения, и затем добавляем отдельные результаты в выходной список.
Мы имеем типовой шаблон в стиле "формы для печенья", который вы можете применять ко многим циклам for. Этот шаблон предназначен для преобразования циклов в конструкцию включения в список, и наоборот. Нужно сказать, что есть еще одно полезное дополнение, которое мы должны внести в этот шаблон, а именно - фильтрация элементов по условиям.
Включения в список могут фильтровать значения, основываясь на некоем произвольном условии, которое определяет, становится результирующее значение частью выходного списка или нет. Приведем пример:
>>> even_squares = [x * x for x in range(10) if x % 2 == 0]
Данное включение в список вычислит список квадратов всех четных целых чисел от нуля до девяти. Использованный здесь операция вычисления остатка (%) возвращает остаток после деления одного числа на другое. В данном примере мы его используем, чтобы проверить, является ли число четным. И оно имеет требуемый результат:
>>> even_squares
[0, 4, 16, 36, 64]
Новое включение в список может быть преобразовано в эквивалентный цикл for аналогично первому примеру:
even_squares = [] for x in range(10): if x % 2 == 0: even_squares.append(x * x)
Давайте попробуем еще слегка обобщить указанный выше шаблон, где включение в список трансформируется в цикл for. На этот раз мы собираемся добавить в наш шаблон фильтрующее условие, которое определяет, какие значения попадут в выходной список. Вот обновленный шаблон включения в список:
values = [expression for item in collection if condition]
И снова, это включение в список можно преобразовать в цикл for с помощью следующего ниже шаблона:
values = [] for item in collection: if condition: values.append(expression)
В очередной раз это преобразование было прямолинейным - мы просто применили обновленный типовой шаблон. Надеюсь, все это рассеяло часть "магии", связанной с тем, как работают включения в список. Они представляют собой полезный инструмент, который все программирующие на Python разработчики должны уметь применять.
Прежде чем мы пойдем дальше, подчеркнем, что Python поддерживает не только включение в список. В нем также имеется аналогичный синтаксический сахар для множеств и словарей.
Вот как выглядит включение в множество:
>>> { x * x for x in range(-9, 10) }
set([64, 1, 36, 0, 49, 9, 16, 81, 25, 4])
В отличие от списков, которые сохраняют порядок следования в них элементов, множества Python имеют тип неупорядоченных коллекций. Поэтому, когда вы будете добавлять элементы в контейнер множества set, вы будете получать более-менее "случайный" порядок следования.
А вот включение в словарь:
>>> { x: x * x for x in range(5) }
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
Оба включения являются весьма полезными инструментами на практике. Правда, относительно включений следует сделать одно предостережение: по мере накопления опыта их применения станет все легче и легче писать трудночитаемый программный код. Если вы не будете осторожны, то вскоре вам, возможно, придется столкнуться с чудовищными включениями в список, в множество и в словарь. Следует помнить, что слишком много хорошего - тоже плохо.
Совет: ограничивайтесь одним уровнем вложенности. За этими границами в большинстве случаев лучше (имея в виду "более легкую удобочитаемость" и "более легкое сопровождение") использовать циклы for.
На следующем шаге мы подитожим изученный материал.