К содержанию

Модуль 4 · Урок 4.6

Включения и функции для коллекций

Списковое включение как запись знакомого цикла, фильтр, sum, min, max, all и any.

5 задач

Списковое включение — короткая запись уже понятного цикла с append, а sum, min, max, all и any заменяют типовые циклы-накопители. Опора — цикл с append из урока о списках и накопители из урока о while: здесь у них появляется короткая запись.

Начнём с квадратов чисел от 1 до 5, а потом превратим строку "10 -2 7" в список чисел.

Включение

Включение записывает правило построения списка, которое раньше давал цикл. Построим квадраты чисел:

squares = []for n in range(1, 6):    squares.append(n * n)print(squares)
[1, 4, 9, 16, 25]

Тот же смысл можно выразить списковым включением — записью правила построения списка:

squares = [n * n for n in range(1, 6)]print(squares)
[1, 4, 9, 16, 25]

Читай по смыслу: «для каждого n из диапазона взять n * n». Хотя выражение результата стоит слева, значение n появляется из части for.

Части цикла и включения одного цвета делают одно и то же: что положить в список, откуда брать значения и какие из них оставить. Меняется только порядок записи.

Фильтр и условное выражение

if после источника отбирает элементы, а условное выражение перед for выбирает значение.

even_squares = [n * n for n in range(1, 8) if n % 2 == 0]print(even_squares)
[4, 16, 36]

if после источника — фильтр: неподходящий элемент вовсе не попадает в результат. Условное выражение перед for решает другую задачу:

a = [-3, 0, 5]labels = ["plus" if x > 0 else "other" for x in a]print(labels)
['other', 'other', 'plus']

Здесь результат имеет столько же элементов, сколько исходный список. Меняется выбираемое значение, а не число элементов.

Несколько for

Несколько for во включении идут в том же порядке, что вложенные циклы.

pairs = [(x, y) for x in range(2) for y in range(3)]print(pairs)
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)]

Части for идут в том же порядке, что обычные вложенные циклы. Внешний — по x, внутренний — по y. Не используй чрезмерно длинные включения: если пояснение становится труднее исходного цикла, лучше оставить цикл.

Та же таблица очерёдности, что для вложенных циклов в уроке о for: внешний x выбирает строку, внутренний y проходит её целиком.

Независимые строки таблицы теперь можно создавать так:

table = [[0] * 3 for _ in range(2)]table[0][0] = 9print(table)
[[9, 0, 0], [0, 0, 0]]

Каждое повторение вычисляет [0] * 3 заново, поэтому строки не связаны — в отличие от [[0] * 3] * 2 из урока о списках. Имя _ — обычная переменная, которой по соглашению обозначают ненужное значение.

sum, min, max, all, any

Бывают включения для множеств и словарей, а частые циклы заменяют готовые функции.

unique_lengths = {len(word) for word in ["кот", "дом", "окно"]}squares_by_number = {n: n * n for n in range(4)}print(unique_lengths)print(squares_by_number)
{3, 4} {0: 0, 1: 1, 2: 4, 3: 9}

Фигурные скобки с одним выражением дают множество, с парой ключ: значение — словарь.

sum(a) складывает элементы, min(a) и max(a) находят крайние значения. sum([]) равно 0, но min([]) и max([]) без значения по умолчанию вызывают ошибку: у пустого набора нет минимума или максимума.

print(sum([]))print(min([]))
0 ValueError: min() iterable argument is empty

all(...) проверяет, что все элементы истинны, any(...) — что истинен хотя бы один. Они останавливаются, когда ответ уже известен. Для пустого источника all возвращает True, а any — False: нет ни одного нарушения первого требования и ни одного подтверждения второго.

numbers = [2, 4, 8]print(all([x % 2 == 0 for x in numbers]))print(any([x < 0 for x in numbers]))
True False

Позднее уберём создание промежуточного списка с помощью генераторного выражения. На этом этапе явный список помогает увидеть логические значения, которые получает функция: [True, True, True] и [False, False, False].

Числа из строки

Числа из строки собирает одно включение. Преобразуем строку "10 -2 7" в список целых одной понятной записью:

line = "10 -2 7"numbers = [int(part) for part in line.split()]print(numbers)print(sum(numbers), min(numbers), max(numbers))
[10, -2, 7] 15 -2 10

В этой строке нет новой магии: split создаёт части, цикл перебирает их, int преобразует каждую, включение собирает список.

Сквозная задача курса — сумма и наибольшее из 5, −2, 10, 7 — теперь записывается одной строкой: print(sum(numbers), max(numbers)) для numbers = [5, -2, 10, 7] выводит 20 10. Внутри sum и max — тот же накопитель и тот же поиск лучшего, что мы писали руками в уроках о while и списках.

Модуль целиком

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

ТипЧто хранитИзменяетсяДоступПостроение включением
strСимволы по порядкуНетПо индексу и срезу—
listЗначения по порядку, с повторамиДаПо индексу и срезу[x * x for x in a]
tupleКороткую записьНетПо индексу, распаковкой—
dictПары «ключ — значение»ДаПо ключу{x: x * x for x in a}
setРазличные значенияДаТолько проверкой in{x % 10 for x in a}

Все пять — перебираемые объекты: по каждому ходит for, к каждому применимы len, in, sum, min, max. Выбор между ними — по нужным действиям, как в уроке о множествах.

Итог

Включения и готовые функции заменяют самые частые циклы одной понятной записью. Вот что понадобится дальше.

  • Включение[выражение for x in источник] — тот же цикл с append, записанный правилом.
  • Фильтр и выборif в конце отбирает элементы; a if c else b в начале выбирает значение.
  • Несколько forНесколько for — вложенные циклы в том же порядке.
  • Множество и словарь{x for …} — множество, {k: v for …} — словарь.
  • Готовые функцииsum пустого — 0, min/max пустого — ошибка; all пустого — True, any пустого — False.

Проверьте себя

Ответь сначала сам, потом открой ответ.

Вопрос 1

Что даст [w[0] for w in "кот дом сад".split()]?

ОтветСкрыть ответ
Ответ

['к', 'д', 'с'].

Вопрос 2

Что даст ["+" if x > 0 else "-" for x in [3, -1, 0]]?

ОтветСкрыть ответ
Ответ

['+', '-', '-'].

Вопрос 3

Сколько элементов в [(a, b) for a in range(3) for b in range(a)]? Выпиши их.

ОтветСкрыть ответ
Ответ

3: (1, 0), (2, 0), (2, 1).

Вопрос 4

Чему равны sum([]), all([]) и any([])? Что сделает max([])?

ОтветСкрыть ответ
Ответ

0, True и False; max([]) — ValueError.

Вопрос 5

Что дадут {n % 4 for n in range(10)} и {c: "abca".count(c) for c in "abca"}?

ОтветСкрыть ответ
Ответ

{0, 1, 2, 3} и {'a': 2, 'b': 1, 'c': 1}.

Практика

Для проверки ответов нужен JavaScript. Условия, подсказки и решения доступны ниже.

Что напечатает программа?

numbers = [int(part) for part in "5 -8 3 0".split()]print(sum(numbers))

Введите число.

Что напечатает программа?

print([n % 3 for n in range(7)])

Запишите список так, как его печатает Python.

Что напечатает программа?

print([x for x in range(20) if x % 7 == 3])

Запишите список так, как его печатает Python.

Что напечатает программа?

numbers = [2, 8, -4]print(all([x % 2 == 0 for x in numbers]))print(any([x < 0 for x in numbers]))

Запишите напечатанные строки через пробел в порядке вывода.

Напиши выражение: сумма квадратов нечётных

Напиши одно выражение со включением и sum, которое находит сумму квадратов нечётных чисел от 1 до 9, напечатай его и запиши ответ.

Введите число.