Генераторы списков (list comprehension) в языке программирования Python

Раздел: Python -> особенности языка

Генераторы списков (List Comprehension) в Python

Генератор списка (list comprehension) позволяет создавать новый список, применяя выражение к каждому элементу итерируемого объекта, возможно, с фильтрацией. Это выразительная и компактная конструкция, которая часто заменяет циклы for с методом append.

Как создать список, применив преобразование к каждому элементу?

Базовый синтаксис: [выражение for элемент in итерируемый_объект if условие]. Условие необязательно.

# Квадраты чисел от 0 до 9
result = [x**2 for x in range(10)]
print(result)  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Python генератор списка (генератор списка в python (list comprehension))

Порядок выполнения: сначала перебираются элементы, для каждого вычисляется выражение, результат добавляется в список. Если указано условие, элемент сначала проверяется, и только при True выполняется преобразование.

Типичные ошибки:

  • Забывают квадратные скобки – получается генераторное выражение, а не список.
  • Условие пишут до цикла, а не после (верно: [x for x in range(10) if x>5], неверно: [if x>5 for x in range(10)]).
  • Изменяют итерируемый объект внутри выражения – может привести к неопределённому поведению.

Как создать список без фильтрации?

Простой генератор – без if. Подходит для преобразования всех элементов.

# Удвоение чисел от 0 до 4
nums = [x*2 for x in range(5)]
print(nums)  # [0, 2, 4, 6, 8]

Как отфильтровать элементы и одновременно преобразовать?

Условие if добавляется после цикла.

# Четные числа, возведённые в квадрат
result = [x**2 for x in range(10) if x % 2 == 0]
print(result)  # [0, 4, 16, 36, 64]

Как использовать условие if-else для каждого элемента?

Тернарный оператор помещается перед циклом.

# Замена чисел на строки: "чётное" или "нечётное"
labels = ["чётное" if x % 2 == 0 else "нечётное" for x in range(5)]
print(labels)  # ['чётное', 'нечётное', 'чётное', 'нечётное', 'чётное']

Путаница в синтаксисе:

Если написать [x if condition else y for z in iterable] – это тернарный оператор для каждого элемента. Не путать с [x for z in iterable if condition], где if фильтрует.

Как создать список комбинаций из нескольких итераций?

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

# Произведения всех пар из двух списков
pairs = [i * j for i in range(1, 4) for j in range(1, 4)]
print(pairs)  # [1, 2, 3, 2, 4, 6, 3, 6, 9]

Неправильный порядок:

Циклы выполняются слева направо: внешний for i, внутренний for j. Изменение порядка даёт другой результат.

Как применить несколько условий?

Можно указать несколько if подряд или объединить через and.

# Числа кратные 3 и больше 10
result = [x for x in range(20) if x % 3 == 0 if x > 10]
# Эквивалентно: [x for x in range(20) if (x % 3 == 0) and (x > 10)]
print(result)  # [12, 15, 18]

Как создать словарь или множество через comprehension?

Синтаксис аналогичен, но используются фигурные скобки (для словаря – с двоеточием).

# Словарь: число -> квадрат
squares_dict = {x: x**2 for x in range(5)}
print(squares_dict)  # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

# Множество: уникальные квадраты
squares_set = {x**2 for x in range(-3, 4)}
print(squares_set)  # {9, 4, 1, 0}

Как создать двумерный список (матрицу)?

Вложенные генераторы: внешний создаёт строки, внутренний – элементы строки.

# Матрица 3x3, заполненная нулями
matrix = [[0 for _ in range(3)] for _ in range(3)]
print(matrix)  # [[0, 0, 0], [0, 0, 0], [0, 0, 0]]

Ошибка с общими ссылками:

Если написать [[0]*3 for _ in range(3)] – это безопасно. Но [[0]*3]*3 создаёт список из трёх ссылок на одну строку, что приводит к неожиданным изменениям.

Генераторы списков улучшают читаемость и часто работают быстрее циклов. Однако для сложной логики или большого количества вложенных условий лучше использовать обычный for с явными комментариями.

Продвинутые примеры использования list comprehension, выходящие за рамки учебных задач.

1. Генератор списка с вызовом функции, обладающей побочным эффектом

Функция может изменять глобальное состояние (например, random.randint). Каждый вызов даёт новый результат.

Пример
import random
random.seed(42)
rolls = [random.randint(1,6) for _ in range(5)]
print(rolls)
[1, 2, 4, 1, 1]

Важно:

Не стоит использовать comprehension для модификации внешних данных – это затрудняет отладку.

2. Генератор списка с enumerate для получения индексов

Удобно, когда нужны и индекс, и значение.

Пример
data = ['a', 'b', 'c']
indexed = [f"{i}:{val}" for i, val in enumerate(data)]
print(indexed)
['0:a', '1:b', '2:c']

3. Транспонирование матрицы с помощью вложенного comprehension и zip

Менее очевидный, но элегантный способ.

Пример
matrix = [[1,2,3],[4,5,6]]
transposed = [[row[i] for row in matrix] for i in range(len(matrix[0]))]
print(transposed)
[[1, 4], [2, 5], [3, 6]]

Тот же результат можно получить через list(zip(*matrix)), но comprehension даёт полный контроль над типом элементов.

4. Генератор списка с тернарным оператором и вложенными циклами

Сложное условие, применяемое к каждой комбинации.

Пример
# Пометить пары: больше суммы порога или нет
pairs = [(i,j, "big" if i+j>5 else "small") for i in range(1,4) for j in range(1,4)]
print(pairs)
[(1,1,'small'), (1,2,'small'), (1,3,'small'), (2,1,'small'), (2,2,'small'), (2,3,'big'), (3,1,'small'), (3,2,'big'), (3,3,'big')]

5. Разница между list comprehension и генераторным выражением

Генераторное выражение (круглые скобки) возвращает итератор, экономя память.

Пример
# list comprehension – сразу создаёт список
lc = [x**2 for x in range(10)]
print(type(lc), lc[:5])

# generator expression – ленивый
ge = (x**2 for x in range(10))
print(type(ge), list(ge)[:5])
<class 'list'> [0, 1, 4, 9, 16]
<class 'generator'> [0, 1, 4, 9, 16]

Генераторное выражение особенно полезно при передаче в функции, принимающие итераторы (например, sum, max).

6. Flatten (разворачивание) вложенного списка переменной глубины

Чистый comprehension не обрабатывает произвольную вложенность, но для двух уровней подходит.

Пример
nested = [[1,2],[3,[4,5]],6]  # неоднородная глубина
# Только для одного уровня вложенности:
flat = [item for sublist in nested if isinstance(sublist, list) for item in sublist]
# Здесь теряются числа 6 и внутренний список [4,5]
print(flat)
[1, 2, 3, [4, 5]]

Для полного flatten потребуется рекурсия или itertools.chain.

7. Генератор списка с условием и обращением к нестандартному атрибуту объекта

Пример: из списка строк оставить только те, что содержат цифры.

Пример
words = ['abc', 'x1y', 'def', '123', '!@#']
digits_words = [w for w in words if any(ch.isdigit() for ch in w)]
print(digits_words)
['x1y', '123']

Внутренняя проверка any использует ещё один генератор.

8. Генератор списка для работы с датами (при помощи datetime)

Создание списка строк с датами на определённый период.

Пример
from datetime import datetime, timedelta
start = datetime(2025, 1, 1)
dates = [(start + timedelta(days=i)).strftime("%d.%m.%Y") for i in range(5)]
print(dates)
['01.01.2025', '02.01.2025', '03.01.2025', '04.01.2025', '05.01.2025']

генератор списка в Python (list comprehension) - comments

En
Python генератор списка (python)