Модуль 4 · Урок 4.6
Включения и функции для коллекций
Списковое включение как запись знакомого цикла, фильтр, sum, min, max, all и any.
Списковое включение — короткая запись уже понятного цикла с 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)Тот же смысл можно выразить списковым включением — записью правила построения списка:
squares = [n * n for n in range(1, 6)]print(squares)Читай по смыслу: «для каждого n из диапазона взять n * n». Хотя выражение результата стоит слева, значение n появляется из части for.
Фильтр и условное выражение
if после источника отбирает элементы, а условное выражение перед for выбирает значение.
even_squares = [n * n for n in range(1, 8) if n % 2 == 0]print(even_squares)if после источника — фильтр: неподходящий элемент вовсе не попадает в результат. Условное выражение перед for решает другую задачу:
a = [-3, 0, 5]labels = ["plus" if x > 0 else "other" for x in a]print(labels)Здесь результат имеет столько же элементов, сколько исходный список. Меняется выбираемое значение, а не число элементов.
Несколько for
Несколько for во включении идут в том же порядке, что вложенные циклы.
pairs = [(x, y) for x in range(2) for y in range(3)]print(pairs)Части for идут в том же порядке, что обычные вложенные циклы. Внешний — по x, внутренний — по y. Не используй чрезмерно длинные включения: если пояснение становится труднее исходного цикла, лучше оставить цикл.
for: внешний x выбирает строку, внутренний y проходит её целиком.Независимые строки таблицы теперь можно создавать так:
table = [[0] * 3 for _ in range(2)]table[0][0] = 9print(table)Каждое повторение вычисляет [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)Фигурные скобки с одним выражением дают множество, с парой ключ: значение — словарь.
sum(a) складывает элементы, min(a) и max(a) находят крайние значения. sum([]) равно 0, но min([]) и max([]) без значения по умолчанию вызывают ошибку: у пустого набора нет минимума или максимума.
print(sum([]))print(min([]))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, 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))В этой строке нет новой магии: 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.
Проверьте себя
Ответь сначала сам, потом открой ответ.
Что даст [w[0] for w in "кот дом сад".split()]?
ОтветСкрыть ответ
['к', 'д', 'с'].
Что даст ["+" if x > 0 else "-" for x in [3, -1, 0]]?
ОтветСкрыть ответ
['+', '-', '-'].
Сколько элементов в [(a, b) for a in range(3) for b in range(a)]? Выпиши их.
ОтветСкрыть ответ
3: (1, 0), (2, 0), (2, 1).
Чему равны sum([]), all([]) и any([])? Что сделает max([])?
ОтветСкрыть ответ
0, True и False; max([]) — ValueError.
Что дадут {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))Введите число.
Подсказка
split даёт части-строки, int превращает каждую в число, sum складывает.
Разбор
Список [5, -8, 3, 0], сумма 5 − 8 + 3 + 0 = 0.
Ответ: 0
Что напечатает программа?
print([n % 3 for n in range(7)])Запишите список так, как его печатает Python.
Подсказка
Для каждого n от 0 до 6 берётся остаток от деления на 3.
Разбор
Остатки 0, 1, 2 повторяются по кругу.
Ответ: [0, 1, 2, 0, 1, 2, 0]
Что напечатает программа?
print([x for x in range(20) if x % 7 == 3])Запишите список так, как его печатает Python.
Подсказка
if в конце оставляет только числа с остатком 3 от деления на 7.
Разбор
Такие числа до 20 — 3, 10 и 17.
Ответ: [3, 10, 17]
Что напечатает программа?
numbers = [2, 8, -4]print(all([x % 2 == 0 for x in numbers]))print(any([x < 0 for x in numbers]))Запишите напечатанные строки через пробел в порядке вывода.
Подсказка
all — все ли истинны, any — есть ли хоть одно истинное.
Разбор
Все три числа чётные — True; −4 отрицательное — True.
Ответ: True True
Напиши выражение: сумма квадратов нечётных
Напиши одно выражение со включением и sum, которое находит сумму квадратов нечётных чисел от 1 до 9, напечатай его и запиши ответ.
Введите число.
Подсказка
Нечётные от 1 до 9 — range(1, 10, 2); квадраты — k * k в начале включения.
Разбор
Например:
print(sum([k * k for k in range(1, 10, 2)]))1 + 9 + 25 + 49 + 81 = 165.
Ответ: 165