Одна строка — одна идея
Ты уже писал этот паттерн несколько раз: начать с пустого списка, пройти по чему-то, может, отфильтровать и положить в результат.
Списковое включение говорит то же самое в одну строку:
Читается слева направо: «новый список, состоящий из n * 2 для каждого n в numbers». Структура — [expression for item in iterable].
Это не специфичный для Python трюк — его называют comprehension, потому что ты декларативно описываешь, что попадает в новый список, а не расписываешь механику сборки.
Добавляем фильтр
Добавь if после части с циклом — получишь фильтр:
Второе читается: «n * n для каждого n в numbers, но только если n нечётное».
Эквивалентный цикл:
Оба варианта нормальны. Включение короче и, когда прочтёшь несколько, реально быстрее сканируется глазом — намерение прямо в одной строке.
Map и filter одновременно
Можно пропустить значения через функцию и одновременно отфильтровать:
Для каждого слова длиннее 3 символов включаем его версию в верхнем регистре.
Вложенные циклы во включении
Два for-а дают пару в стиле декартова произведения:
Порядок такой же, как если бы ты вложил циклы: первый for — внешний, второй — внутренний. Читается слева направо, как и эквивалентный цикл с отступами.
Два уровня — примерно предел, после которого обычный цикл читается лучше. Дошёл до трёх — переключайся обратно на циклы.
Включения словарей и множеств
Та же идея, другие скобки:
Включения множеств на первый взгляд выглядят так же, как включения словарей, — разница в key: value против одного выражения. Фигурные плюс : — словарь; фигурные без : — множество.
Generator-выражения
Близнец спискового включения, но с круглыми скобками вместо квадратных — и, что критично, он не строит список:
Заметь: генератор мы передали прямо в sum() и any() — без лишних скобок. Generator-выражения — правильный инструмент, когда вызывающей стороне достаточно один раз проитерироваться. Для больших коллекций они экономят память по сравнению с полным списком.
Когда взять обычный цикл
Включения соблазнительны. Технически туда можно напихать много. Не стоит.
Тянись к обычному циклу, когда:
- Преобразование состоит из нескольких шагов. Если нужны промежуточные переменные — распиши.
- Есть сложная обработка ошибок или ветвление.
- Любой читатель вынужден задержаться на строке дважды, чтобы её понять.
Правило, которое я применяю: если могу прочесть включение на одном вдохе и понять, что оно делает, — оно остаётся. Спотыкаюсь — переписываю в цикл.
Некоторые включения выглядят умно, но изматывают:
Результат тот же. Цикл — пять строк вместо одной, но «длиннее» ≠ «хуже».
Несколько паттернов, которые пригодятся
Эти пять паттернов покрывают удивительно большую долю повседневной работы с данными.
Дальше
Ты уже видел основные коллекционные типы и включение, которое их связывает. Следующая глава — функции: упаковка поведения в именованные, переиспользуемые единицы.
Часто задаваемые вопросы
Что такое списковое включение в Python?
Компактный синтаксис для построения нового списка из существующего итерируемого. [x * 2 for x in numbers] создаёт новый список, где каждое число удвоено. Можно и фильтровать: [x for x in numbers if x > 0].
Когда лучше не использовать списковое включение?
Когда страдает читаемость. Если выражение внутри сложное или глубоко вложенное — обычный for-цикл с нормальными именами переменных понятнее. Включения для простых преобразований — отображения и фильтрации. Всё, что сложнее, лучше в полном цикле.
В чём разница между списковым включением и generator-выражением?
Списковое включение строит весь список в памяти. Generator-выражение (тот же синтаксис, но в круглых скобках) выдаёт по одному элементу. Используй генератор, когда результаты передаются чему-то, что итерирует один раз — вроде sum(...) — чтобы не материализовать список, который тут же выбросишь.