Списковые включения
Синтаксис списковых включений Python, фильтрация, вложенные циклы, включения словарей и множеств, а также когда их использовать.
Списковые включения Python
Списковое включение — это краткий способ построить новый список, применяя выражение к каждому элементу итерируемого объекта, с возможностью фильтрации элементов по условию — всё в одну читаемую строку. На этой странице рассматривается полный синтаксис, распространённые шаблоны, включения словарей и множеств, а также рекомендации о том, когда лучше использовать обычный цикл for.
Синтаксис
Общая форма спискового включения:
new_list = [expression for item in iterable if condition]| Часть | Роль |
|---|---|
expression | Значение, помещаемое в новый список для каждого item |
item | Переменная цикла, принимающая поочерёдно каждое значение из iterable |
iterable | Любая последовательность или итерируемый объект: список, кортеж, строка, range и т. д. |
if condition | Необязательный фильтр — включаются только элементы, для которых условие равно True |
Часть if condition является необязательной. Если она опущена, каждый элемент итерируемого объекта порождает одну запись в новом списке.
Базовый пример: возведение чисел в квадрат
Эквивалентный цикл for требует трёх строк; списковое включение делает то же самое в одну:
Фильтрация с условием
Добавьте клаузу if после итерируемого объекта, чтобы оставить только элементы, удовлетворяющие условию:
Можно использовать любой предикат. Например, оставить слова длиннее четырёх символов:
words = ['hi', 'hello', 'world', 'python', 'ai']
long_words = [w for w in words if len(w) > 4]
print(long_words) # ['hello', 'world', 'python']if/else в выражении (тернарный оператор)
Когда нужно преобразовать каждый элемент, применяя разную логику в зависимости от условия, поместите if/else внутрь части выражения (перед for), а не после итерируемого объекта:
numbers = range(1, 6)
labels = ['even' if x % 2 == 0 else 'odd' for x in numbers]
print(labels) # ['odd', 'even', 'odd', 'even', 'odd']Обратите внимание на разницу:
[expr for x in it if cond]— фильтр: пропускает элементы, для которыхcondравноFalse[a if cond else b for x in it]— преобразование: каждый элемент порождает значение, выбираемое поcond
Работа со строками
Списковые включения работают с любым итерируемым объектом, включая строки и списки строк:
words = ['hello', 'world', 'python']
upper_words = [w.upper() for w in words]
print(upper_words) # ['HELLO', 'WORLD', 'PYTHON']Извлечение отдельных символов, удовлетворяющих условию:
vowels = [ch for ch in 'programming' if ch in 'aeiou']
print(vowels) # ['o', 'a', 'i']Вложенные циклы
Списковые включения поддерживают несколько клауз for, эквивалентных вложенным циклам. Крайний левый for является внешним циклом:
Распространённый случай использования вложенных списковых включений — преобразование двумерного списка (матрицы) в одномерный:
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [n for row in matrix for n in row]
print(flat) # [1, 2, 3, 4, 5, 6, 7, 8, 9]Совет по чтению: мысленно разворачивайте вложенные включения как вложенные циклы for в том же порядке слева направо.
Включения словарей
Та же идея применяется к словарям с использованием {} и выражения key: value:
keys = ['a', 'b', 'c']
values = [1, 2, 3]
mapping = {k: v for k, v in zip(keys, values)}
print(mapping) # {'a': 1, 'b': 2, 'c': 3}Поменять ключи и значения в существующем словаре:
original = {'name': 'Alice', 'city': 'Paris'}
inverted = {v: k for k, v in original.items()}
print(inverted) # {'Alice': 'name', 'Paris': 'city'}Включения множеств
Используйте {} с одним выражением (без двоеточия), чтобы построить множество, которое автоматически удаляет дубликаты:
numbers = [1, 2, 2, 3, 3, 3, 4]
unique_squares = {x ** 2 for x in numbers}
print(unique_squares) # {1, 4, 9, 16}Результат не упорядочен (порядок элементов в множестве не гарантирован), поэтому не полагайтесь на выводимую последовательность.
Производительность
Списковые включения, как правило, быстрее эквивалентных циклов for, вызывающих .append(), поскольку интерпретатор может оптимизировать внутренний цикл на уровне C. Для измерения времени выполнения собственного кода используйте модуль timeit:
import timeit
loop_time = timeit.timeit(
'result = []\nfor x in range(1000):\n result.append(x**2)',
number=10000
)
comp_time = timeit.timeit(
'[x**2 for x in range(1000)]',
number=10000
)
print(f'Loop: {loop_time:.3f}s')
print(f'Comprehension: {comp_time:.3f}s')
# Comprehension is typically 20-40% fasterПреимущество в производительности уменьшается или исчезает, когда само выражение работает медленно (сетевой ввод-вывод, тяжёлые вычисления). Профилируйте код перед оптимизацией.
Когда лучше использовать обычный цикл for
Списковые включения подходят не всегда:
| Ситуация | Предпочтительный вариант |
|---|---|
| Одиночное преобразование или фильтрация | Списковое включение |
| Множество побочных эффектов на каждой итерации (логирование, изменение состояния) | Цикл for |
| Сложная логика, требующая нескольких операторов | Цикл for |
| Глубоко вложенные включения (более двух уровней) | Цикл for — читаемость важнее |
| Результат не сохраняется (нужен только обход) | Генераторное выражение ((x for x in ...)) |
Главный признак того, что пора вернуться к циклу for, — когда вам приходится пристально вглядываться в включение, чтобы понять, что оно делает.
Подводные камни
В Python 3 утечки переменных не происходит. Переменная цикла в включении ограничена областью видимости самого включения:
x = 'original'
result = [x * 2 for x in range(3)]
print(x) # 'original' — not overwritten by the comprehension's xИзбегайте глубоко вложенных включений. Более двух клауз for в одном включении ухудшают читаемость при минимальной пользе. Разбивайте их на именованные промежуточные списки или циклы for.
Генераторные выражения экономят память. Если вам нужно пройти по результату только один раз (например, передать его в sum() или max()), замените [] на (), чтобы получить генератор, выдающий элементы по одному:
total = sum(x ** 2 for x in range(1, 1001))
print(total) # 333833500