Простой Python — страница 14 из 66

Включение — это компактный способ создать структуру данных из одного или более итераторов. Включения позволяют вам объединять циклы и условные проверки, не используя при этом громоздкий синтаксис. Если вы применяете включение, то можно сказать, что уже неплохо знаете Python. Иными словами, это одна из характерных особенностей данного языка.

Включение списков

Вы можете создать список целых чисел от 1 до 5, добавляя их туда по одному за раз, например, так:

>>> number_list = []

>>> number_list.append(1)

>>> number_list.append(2)

>>> number_list.append(3)

>>> number_list.append(4)

>>> number_list.append(5)

>>> number_list

[1, 2, 3, 4, 5]

Или же вы могли бы использовать итератор и функцию range():

>>> number_list = []

>>> for number in range(1, 6):

…·····number_list.append(number)

>>> number_list

[1, 2, 3, 4, 5]

Или же преобразовать в список сам результат работы функции range():

>>> number_list = list(range(1, 6))

>>> number_list

[1, 2, 3, 4, 5]

Все эти подходы абсолютно корректны с точки зрения Python и сгенерируют одинаковый результат. Однако более характерным для Python является создание списка с помощью включения списка. Простейшая форма такого включения выглядит так:

[выражение for элемент in итерабельный объект]

Вот так выглядит включение списка целых чисел:

>>> number_list = [number for number in range(1,6)]

>>> number_list

[1, 2, 3, 4, 5]

В первой строке вам нужно, чтобы первая переменная number сформировала значения для списка: следует разместить результат работы цикла в переменной number_list. Вторая переменная number является частью цикла for. Чтобы показать, что первая переменная number является выражением, попробуем такой вариант:

>>> number_list = [number-1 for number in range(1,6)]

>>> number_list

[0, 1, 2, 3, 4]

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

[выражение for элемент in итерабельный объект if условие]

Создадим новое включение, которое создает список, состоящий только из четных чисел, расположенных в диапазоне от 1 до 5 (помните, что выражение number % 2 имеет значение True для четных чисел и False для нечетных):

>>> a_list = [number for number in range(1,6) if number % 2 == 1]

>>> a_list

[1, 3, 5]

Теперь включение выглядит чуть более компактно, чем его традиционный аналог:

>>> a_list = []

>>> for number in range(1,6):

…·····if number % 2 == 1:

…·········a_list.append(number)

>>>··a_list

[1, 3, 5]

Наконец, точно так же, как и в случае вложенных циклов, можно написать более чем один набор операторов for… в соответствующем выделении. Чтобы продемонстрировать это, сначала создадим старый добрый вложенный цикл и выведем на экран результат:

>>> rows = range(1,4)

>>> cols = range(1,3)

>>> for row in rows:

…·····for col in cols:

…·········print(row, col)

1 1

1 2

2 1

2 2

3 1

3 2

Теперь воспользуемся включением и присвоим его переменной cells, создавая тем самым список кортежей (row, col):

>>> rows = range(1,4)

>>> cols = range(1,3)

>>> cells = [(row, col) for row in rows for col in cols]

>>> for cell in cells:

…·····print(cell)

(1, 1)

(1, 2)

(2, 1)

(2, 2)

(3, 1)

(3, 2)

Кстати, вы можете воспользоваться распаковкой кортежа, чтобы выдернуть значения row и col из каждого кортежа по мере итерирования по списку cells:

>>> for row, col in cells:

…·····print(row, col)

1 1

1 2

2 1

2 2

3 1

3 2

Фрагменты for row… и for col… во включении также могут иметь свои проверки if.

Включение словаря

Для словарей также можно создать включение. Простейшая его форма выглядит привычно:

{ выражение_ключа: выражение_значения for выражение in итерабельный объект }

Как и в случае с включениями списка, выделения словарей также имеют проверки if и несколько операторов for:

>>> word = 'letters'

>>> letter_counts = {letter: word.count(letter) for letter in word}

>>> letter_counts

{'l': 1, 'e': 2, 't': 2, 'r': 1, 's': 1}

Мы запускаем цикл, проходя по каждой из семи букв в строке letters, и считаем, сколько раз появляется эта буква. Два наших вызова word.count(letter) — это лишь пустая трата времени, поскольку нам нужно подсчитать буквы «e» и «t» два раза. Но когда мы считаем буквы «e» во второй раз, то не причиняем вреда, поскольку лишь заменяем уже существующую запись в словаре; то же относится и к подсчету букв «t». Следующий способ решения задачи более характерен для Python:

>>> word = 'letters'

>>> letter_counts = {letter: word.count(letter) for letter in set(word)}

>>> letter_counts

{'t': 2, 'l': 1, 'e': 2, 'r': 1, 's': 1}

Ключи словаря располагаются в ином, чем в предыдущем примере, порядке, поскольку итерирование по результату работы функции set(word) возвращает буквы в другом порядке, нежели итерирование по строке word.

Включение множества

Никто не хочет оказаться обиженным, поэтому даже у множеств есть включения. Простейшая версия выглядит как включение списка или словаря, которые вы только что видели:

{ выражение for выражение in итерабельный объект}

Более длинные версии (проверки if, множественные операторы for) также доступны для множеств:

>>> a_set = {number for number in range(1,6) if number % 3 == 1}

>>> a_set

{1, 4}

Включение генератора

Для кортежей не существует включений. Вы могли подумать, что замена квадратных скобок у выделения списка на круглые создаст включение кортежа. Может даже показаться, что это работает, поскольку исключение не будет сгенерировано, если вы напишете следующее:

>>> number_thing = (number for number in range(1, 6))

В круглые скобки заключено включение генератора, оно возвращает объект генератора:

>>> type(number_thing)

Сами генераторы мы рассмотрим более детально позже в данной главе. Применение генераторов — это один из способов предоставить данные итератору.

Вы можете итерировать непосредственно по этому объекту генератора, как показано здесь:

>>> for number in number_thing:

…·····print(number)

1

2

3

4

5

Или же вы можете обернуть вызов list() вокруг включения генератора, чтобы заставить его работать как включение списка:

>>> number_list = list(number_thing)

>>> number_list

[1, 2, 3, 4, 5]


Генератор может быть запущен лишь однажды. Списки, множества и словари существуют в памяти, но генератор создает свои значения во время работы программы и выдает их по одному за раз через итератор. Он не запоминает их, поэтому вы не можете перезапустить или создать резервную копию генератора.


Если вы попробуете проитерировать по генератору заново, то обнаружите, что он истощен:

>>> try_again = list(number_thing)

>>> try_again

[]

Вы можете создать генератор из включения генератора, как мы сделали это здесь, или из функции генератора. Сначала мы поговорим о функциях в целом, а затем рассмотрим частный случай — функции генератора.

Функции