List comprehension в Python: шпаргалка

Зачем знать list comprehension

Одна из самых любимых тем интервьюеров на собесе аналитика с Python. Конструкция, которая превращает 5 строк цикла в одну — но требует понимания.

Базовый синтаксис

[expression for item in iterable]

Пример:

nums = [1, 2, 3, 4, 5]
squares = [x**2 for x in nums]
# [1, 4, 9, 16, 25]

Эквивалент:

squares = []
for x in nums:
    squares.append(x**2)

С условием (фильтром)

[expression for item in iterable if condition]

Пример:

nums = [1, 2, 3, 4, 5, 6]
even_squares = [x**2 for x in nums if x % 2 == 0]
# [4, 16, 36]

С if-else в expression

[expression_if_true if condition else expression_if_false for item in iterable]

Пример:

nums = [1, -2, 3, -4, 5]
abs_nums = [x if x > 0 else -x for x in nums]
# [1, 2, 3, 4, 5]

Важно: if-фильтр идёт после for. if-elseперед for.

Вложенные циклы

pairs = [(x, y) for x in [1, 2, 3] for y in ['a', 'b']]
# [(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b'), (3, 'a'), (3, 'b')]

Порядок как в обычном цикле: внешний for идёт первым.

Плоский массив из матрицы

matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [x for row in matrix for x in row]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

Попробовать силы на подобных вопросах проще всего в тренажёре Карьерник — прямо в Telegram, без регистрации через сайт.

Dict comprehension

names = ['Иван', 'Петр', 'Аня']
name_len = {name: len(name) for name in names}
# {'Иван': 4, 'Петр': 4, 'Аня': 3}

Swap key/value

d = {'a': 1, 'b': 2, 'c': 3}
reversed_d = {v: k for k, v in d.items()}
# {1: 'a', 2: 'b', 3: 'c'}

Set comprehension

nums = [1, 2, 3, 1, 2, 4]
unique_squares = {x**2 for x in nums}
# {1, 4, 9, 16}

Generator expression

# Круглые скобки вместо квадратных
gen = (x**2 for x in range(10**6))

Не хранит весь список в памяти — итерирует по одному. Полезно для больших данных.

Практические примеры

1. Преобразование списка строк

words = ['hello', 'WORLD', 'Python']
lower = [w.lower() for w in words]
# ['hello', 'world', 'python']

2. Фильтрация по длине

words = ['a', 'bb', 'ccc', 'dddd']
long = [w for w in words if len(w) > 2]
# ['ccc', 'dddd']

3. Парсинг CSV-строки

row = 'Иван,25,Москва'
cleaned = [x.strip() for x in row.split(',')]
# ['Иван', '25', 'Москва']

4. Извлечение из списка словарей

users = [{'name': 'Иван', 'age': 25}, {'name': 'Петр', 'age': 30}]
names = [u['name'] for u in users]
# ['Иван', 'Петр']

5. Conditional replacement

nums = [10, -5, 3, -8, 12]
no_negative = [n if n > 0 else 0 for n in nums]
# [10, 0, 3, 0, 12]

6. Транспонирование матрицы

matrix = [[1, 2, 3], [4, 5, 6]]
transposed = [[row[i] for row in matrix] for i in range(len(matrix[0]))]
# [[1, 4], [2, 5], [3, 6]]

7. Флаги для категорий

ages = [10, 25, 45, 70]
categories = ['child' if a < 18 else 'adult' if a < 65 else 'senior' for a in ages]
# ['child', 'adult', 'adult', 'senior']

8. Уникальные элементы с условием

nums = [1, 2, 3, 1, 2, 3, 4, 4]
unique_even = {x for x in nums if x % 2 == 0}
# {2, 4}

9. Индексация в цикле через enumerate

words = ['a', 'b', 'c']
indexed = [f'{i}: {w}' for i, w in enumerate(words)]
# ['0: a', '1: b', '2: c']

10. Пропуск «битых» данных

data = ['123', 'abc', '456', None, '789']
nums = [int(x) for x in data if x and x.isdigit()]
# [123, 456, 789]

Когда НЕ использовать

1. Слишком длинно

# Непонятно — перепишите в обычный цикл
result = [x*2 if x > 0 and x < 100 and x%3 == 0 else -x if x < 0 else 0 for x in nums for y in other]

2. С побочными эффектами

# ❌ Плохо
[print(x) for x in nums]

# ✅ Используйте обычный цикл
for x in nums:
    print(x)

3. Сложные условия

Если надо 3+ if/else, может быть чище функция с циклом.

Пройти 30–50 задач по теме за вечер можно в Telegram-тренажёре. Это то, что отличает «знаю» от «уверенно отвечу на собесе».

Производительность

List comprehension обычно быстрее обычного цикла на 10-30%:

# Быстрее
squares = [x**2 for x in range(10**6)]

# Медленнее
squares = []
for x in range(10**6):
    squares.append(x**2)

Причина: оптимизированная byte-code реализация, без вызовов .append.

Но: для очень больших данных используйте generator expression или NumPy (быстрее в 10-100x).

10 задач с собесов

1. Квадраты чётных чисел

[x**2 for x in nums if x % 2 == 0]

2. Длины слов

[len(w) for w in words]

3. Инвертировать словарь

{v: k for k, v in d.items()}

4. Уникальные длины в наборе слов

{len(w) for w in words}

5. Преобразование в CamelCase

result = ''.join([w.title() for w in 'hello world'.split()])
# 'HelloWorld'

6. Подсчёт частот через dict comprehension

from collections import Counter
c = Counter('hello')
{char: c[char] for char in c}

7. Все подстроки

s = 'abc'
subs = [s[i:j] for i in range(len(s)) for j in range(i+1, len(s)+1)]
# ['a', 'ab', 'abc', 'b', 'bc', 'c']

8. Парсинг JSON-строк в dict

import json
jsons = ['{"x":1}', '{"x":2}']
data = [json.loads(j) for j in jsons]

9. Фильтр по regex

import re
emails = ['a@b.c', 'not-email', 'x@y.z']
valid = [e for e in emails if re.match(r'^\S+@\S+\.\S+$', e)]

10. Групповая обработка

from itertools import groupby
data = [(1, 'a'), (1, 'b'), (2, 'c')]
grouped = {k: [v for _, v in vs] for k, vs in groupby(data, key=lambda x: x[0])}

Как тренироваться

List comprehension учится за час, закрепляется на практике. Решайте простые задачи: «квадраты», «фильтрация», «инверт словаря».

Совет: на собесе, если переписываете цикл в comprehension — делайте это, только если код стал читабельнее. «Круто» — не цель.

Читайте также

FAQ

list comprehension или map/filter?

Comprehension читабельнее в большинстве случаев. map/filter — когда уже есть готовая функция: list(map(int, strs)).

Когда generator вместо list?

Когда не нужен весь результат в памяти. Для потоковой обработки больших данных, для одноразовой итерации.

Как сделать dict из двух списков?

keys = ['a', 'b', 'c']
vals = [1, 2, 3]
d = dict(zip(keys, vals))
# {'a': 1, 'b': 2, 'c': 3}

Вложенные comprehension тормозят?

Не сильно. Но читабельность страдает после 2 уровней. 3+ уровней — перепишите в обычный цикл.