Списковые включения
Чему вы научитесь:
- строить новый список из старого одной строкой — списковым включением;
- отбирать элементы фильтром
ifи не путать его с условным выражением; - собирать включениями словари и множества;
- считать сумму, минимум и максимум без лишнего списка — генераторным выражением.

Представьте конвейер на фабрике. По ленте одна за другой едут плитки с числами 1, 2, 3, 4. На пути стоит пресс с надписью n * n: на каждой плитке он выбивает квадрат её числа. В конце ленты в ящик ложатся плитки 1, 4, 9, 16 — в том же порядке, в каком ехали.
В программах такая работа встречается постоянно: взять каждый элемент списка, что-то с ним сделать и сложить результаты в новый список. Вы уже умеете делать это циклом с append. Сравните его с одной строкой, которая делает то же самое:
nums = [1, 2, 3, 4]
squares = []
for n in nums:
squares.append(n * n)
print(squares) # [1, 4, 9, 16]
squares = [n * n for n in nums]
print(squares) # [1, 4, 9, 16]Давайте разберём обе версии:
- Цикл создаёт пустой список
squares, перебираетnumsи для каждогоnдобавляетn * nчерезappend. - Строка
[n * n for n in nums]делает ту же работу сама: перебираетnumsи собирает квадраты в новый список. - Обе версии печатают
[1, 4, 9, 16].
Запись [n * n for n in nums] называют списковым включением. Она читается почти как фраза: «n * n для каждого n из nums». В ней нет пустого списка и append, а имя squares встречается один раз.
Как устроено включение
Включение повторяет конвейер. Внутри квадратных скобок две части:
- сначала выражение — пресс, который делает из элемента новое значение;
- затем заголовок цикла
forбез двоеточия — лента, которая подаёт элементы по одному.
Python перебирает nums, для каждого элемента вычисляет выражение и складывает результаты в новый список по порядку. Здесь аналогия перестаёт работать: настоящий пресс меняет саму плитку, а исходный список nums остаётся прежним. Включение строит новый список рядом со старым.
Включение — выражение, как и условное выражение. Его можно присвоить переменной, передать в print или поставить после return. Перебирать можно любой итерируемый объект — список, строку, range:
print([c.upper() for c in "abc"]) # ['A', 'B', 'C']
print([i * 10 for i in range(4)]) # [0, 10, 20, 30]
print([len(w) for w in ["ox", "sun"]]) # [2, 3]Условие-фильтр
Перед прессом можно поставить контролёра: он пропускает дальше только подходящие плитки. Во включении эту роль играет if с условием после заголовка for. В результат попадают только те элементы, для которых условие истинно:
nums = [4, -1, 7, 0, -3]
print([n * 2 for n in nums if n > 0]) # [8, 14]
print([n if n > 0 else 0 for n in nums]) # [4, 0, 7, 0, 0]Разберём первую строку:
for n in numsподаёт числа по одному:4,-1,7,0,-3.if n > 0пропускает дальше только4и7.- Выражение
n * 2вычисляется лишь для них, и в список попадают8и14.
Элемент, который не прошёл проверку, выпадает целиком, поэтому результат короче исходного списка. Такое включение заменяет цикл, в котором append стоит внутри if.
Во второй строке фильтра нет: на месте выражения стоит условное выражение n if n > 0 else 0. Оно выбирает значение для каждого элемента, и длина списка сохраняется. Все числа, кроме положительных, заменены на 0, а не выброшены.
Цикл наизнанку
Самое непривычное во включении — порядок. Читают его слева направо, а работает оно в другом порядке:
- Сначала
forберёт очередной элемент. - Затем
ifрешает, оставить ли его. - Последним вычисляется выражение слева, и его значение добавляется в список.
Включение — это цикл с append, вывернутый наизнанку. То, что в цикле стоит в самой глубине, append(n * 2), во включении вынесено вперёд. Нажмите «Следующий шаг» и следите, в каком порядке работают строки цикла: включение в последней строке проходит те же шаги.
1nums = [4, -1, 7]2result = []3for n in nums:4 if n > 0:5 result.append(n * 2)6print(result)7print([n * 2 for n in nums if n > 0])
Список nums — лента с тремя плитками.
У включения есть ещё одно отличие от цикла: его переменная живёт только внутри скобок. После цикла for x in nums имя x остаётся со значением последнего элемента. После [x * 2 for x in nums] имени x снаружи нет, и print(x) сообщит об ошибке NameError. Этот вопрос часто задают на собеседовании.
Два цикла for
Во включении может стоять несколько заголовков for подряд. Они вложены друг в друга в том же порядке, что и обычные циклы:
- первый
for— внешний; - второй
for— внутренний, и он может перебирать переменную внешнего.
Так [ch for w in ["ab", "cd"] for ch in w] даёт ['a', 'b', 'c', 'd']. Внешний цикл берёт слово, а внутренний — его буквы. Это тот же результат, что у цикла for w in …, внутри которого цикл for ch in w добавляет ch через append.
Порядок заголовков обратен фразе «буквы каждого слова». В записи [ch for ch in w for w in words] имени w ещё нет, и Python сообщает об ошибке NameError. Когда в одной строке два for и условие, понятнее обычные вложенные циклы.
Частая ошибка
Фильтр написали перед for, как условное выражение без else:
nums = [4, -1, 7, 0, -3]
positive = [n if n > 0 for n in nums] # ✗SyntaxError: expected 'else' after 'if' expressionПеред for Python ждёт выражение, а n if n > 0 без else выражением не является, поэтому файл не запускается. Условие, которое отбрасывает элементы, пишут после заголовка цикла: [n for n in nums if n > 0].
Главное
- Списковое включение
[n * n for n in nums]строит новый список, а исходный не меняет. - Фильтр
ifстоит послеforи выбрасывает элементы; условное выражение сelseстоит передforи сохраняет длину. - Включение — цикл с
appendнаизнанку: сначалаfor, затемif, последним — выражение слева. - Несколько
forвложены в порядке записи: первый — внешний.
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.