Перейти к содержимому
Editor

List comprehension в Python: все виды включений в одном уроке

Средний30 min8 упр.115 XP

Этот урок переведён с английского оригинала.Read in EnglishLeer en españolСообщить об ошибке в переводе

0/8 упр.

Представьте, что у вас есть список из 100 чисел и нужен новый список, в котором останутся только чётные числа, причём каждое удвоено. Обычным циклом это четыре строки. Включением в Python это одна строка.

Включения (comprehensions) входят в число самых любимых возможностей Python. Они позволяют создавать новые списки, словари и множества, описывая, что вы хотите получить, а не расписывая по шагам, как это построить.

В этом уроке вы изучите все четыре вида включений (для списков, словарей, множеств и генераторные выражения), а также фильтрацию, условия и важный вопрос: когда включения использовать НЕ стоит.

Списковые включения: самый частый приём

Списковое включение (list comprehension) создаёт новый список, применяя выражение к каждому элементу существующей последовательности. Синтаксис такой: [expression for item in iterable].

Это как компактный цикл for, который строит список. Вместо того чтобы создавать пустой список и добавлять в него элементы внутри цикла, вы описываете результат одной строкой.

Обычный цикл (4 строки)
numbers = [1, 2, 3, 4, 5]
squares = []
for n in numbers:
    squares.append(n ** 2)
print(squares)  # [1, 4, 9, 16, 25]
Списковое включение (1 строка)
numbers = [1, 2, 3, 4, 5]
squares = [n ** 2 for n in numbers]
print(squares)  # [1, 4, 9, 16, 25]
Простые списковые включения
# Square each number
numbers = [1, 2, 3, 4, 5]
squares = [n ** 2 for n in numbers]
print(squares)
# Convert names to uppercase
names = ['alice', 'bob', 'charlie']
upper_names = [name.upper() for name in names]
print(upper_names)
# Get the length of each word
words = ['hello', 'world', 'python', 'is', 'fun']
lengths = [len(w) for w in words]
print(lengths)
Вывод
[1, 4, 9, 16, 25]
['ALICE', 'BOB', 'CHARLIE']
[5, 5, 6, 2, 3]

Условия во включениях

Можно добавить условие if, чтобы отфильтровать элементы. В новый список попадут только те элементы, которые проходят проверку. Синтаксис такой: [expression for item in iterable if condition].

Фильтрация с помощью if
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Keep only even numbers
evens = [n for n in numbers if n % 2 == 0]
print(evens) # [2, 4, 6, 8, 10]
# Keep only words longer than 3 characters
words = ['hi', 'hello', 'hey', 'world', 'ok', 'python']
long_words = [w for w in words if len(w) > 3]
print(long_words) # ['hello', 'world', 'python']
Вывод
[2, 4, 6, 8, 10]
['hello', 'world', 'python']

Можно также использовать выражение if-else, чтобы преобразовывать элементы по-разному в зависимости от условия. Но обратите внимание, что синтаксис меняется: if-else ставится перед for, а не после него.

if-else во включениях
numbers = [1, 2, 3, 4, 5, 6]
# Label each number as even or odd
labels = ['even' if n % 2 == 0 else 'odd' for n in numbers]
print(labels) # ['odd', 'even', 'odd', 'even', 'odd', 'even']
# Cap scores at 100
raw_scores = [85, 110, 92, 105, 78]
capped = [min(s, 100) for s in raw_scores]
print(capped) # [85, 100, 92, 100, 78]
Вывод
['odd', 'even', 'odd', 'even', 'odd', 'even']
[85, 100, 92, 100, 78]

Включения словарей

Включения словарей (dict comprehension) работают так же, как списковые, но создают словари. В них используются фигурные скобки и выражение вида key: value. Синтаксис такой: {key: value for item in iterable}.

Простые включения словарей
# Create a dict of numbers and their squares
squares = {n: n ** 2 for n in range(1, 6)}
print(squares) # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
# Create a dict from two lists using zip()
names = ['Alice', 'Bob', 'Charlie']
scores = [95, 87, 72]
grade_book = {name: score for name, score in zip(names, scores)}
print(grade_book) # {'Alice': 95, 'Bob': 87, 'Charlie': 72}
Вывод
{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
{'Alice': 95, 'Bob': 87, 'Charlie': 72}

Как и в списковых включениях, здесь можно фильтровать с помощью if. Это удобно, когда нужно получить часть словаря.

Включения словарей с фильтрацией и преобразованием
prices = {'apple': 1.50, 'banana': 0.75, 'cherry': 3.00, 'date': 5.00}
# Keep only items under $2
affordable = {fruit: price for fruit, price in prices.items() if price < 2}
print(affordable) # {'apple': 1.5, 'banana': 0.75}
# Swap keys and values
flipped = {price: fruit for fruit, price in prices.items()}
print(flipped) # {1.5: 'apple', 0.75: 'banana', 3.0: 'cherry', 5.0: 'date'}
Вывод
{'apple': 1.5, 'banana': 0.75}
{1.5: 'apple', 0.75: 'banana', 3.0: 'cherry', 5.0: 'date'}

Включения множеств

Включения множеств (set comprehension) создают множества, то есть коллекции без повторов. В них тоже фигурные скобки, как во включениях словарей, но без двоеточия. Синтаксис такой: {expression for item in iterable}.

Включения множеств
# Get unique first letters from a list of names
names = ['Alice', 'Anna', 'Bob', 'Charlie', 'Chloe']
first_letters = {name[0] for name in names}
print(first_letters) # {'A', 'B', 'C'}
# Get unique word lengths
sentence = 'the quick brown fox jumps over the lazy dog'
lengths = {len(word) for word in sentence.split()}
print(sorted(lengths)) # [3, 4, 5]
Вывод
{'B', 'C', 'A'}
[3, 4, 5]

Главное отличие от списковых включений в том, что множества сами убирают повторы. И «Alice», и «Anna» начинаются на «A», но в множестве будет только одна «A».

Генераторные выражения: включения, которые экономят память

Генераторное выражение выглядит как списковое включение, но записывается в круглых скобках вместо квадратных. Главное отличие: оно не создаёт все элементы сразу, а выдаёт их по одному, по мере надобности.

Это важно, когда вы работаете с очень большими данными. Списковое включение на миллион элементов создаёт в памяти список из миллиона элементов. Генераторное выражение почти не занимает памяти: оно вычисляет каждый элемент только тогда, когда вы его запрашиваете.

Списковое включение и генераторное выражение
# List comprehension - builds the entire list in memory
squares_list = [n ** 2 for n in range(5)]
print(squares_list) # [0, 1, 4, 9, 16]
print(type(squares_list)) # <class 'list'>
# Generator expression - generates items on demand
squares_gen = (n ** 2 for n in range(5))
print(squares_gen) # <generator object ...>
print(type(squares_gen)) # <class 'generator'>
# You can loop through a generator
for sq in squares_gen:
print(sq, end=' ')
print()
Вывод
[0, 1, 4, 9, 16]
<class 'list'>
<generator object <genexpr> at 0x4fc5898>
<class 'generator'>
0 1 4 9 16

Генераторные выражения часто используют внутри функций вроде sum(), max() и min(). Если генератор единственный аргумент функции, лишние скобки можно не писать.

Генераторные выражения внутри функций
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Sum of squares using a generator (no extra parentheses needed)
total = sum(n ** 2 for n in numbers)
print(f'Sum of squares: {total}')
# Check if any number is negative
has_negative = any(n < 0 for n in numbers)
print(f'Has negative: {has_negative}')
# Check if all numbers are positive
all_positive = all(n > 0 for n in numbers)
print(f'All positive: {all_positive}')
Вывод
Sum of squares: 385
Has negative: False
All positive: True

Вложенные включения

Чтобы описать вложенные циклы, можно поставить одну часть for вслед за другой. Сначала идёт внешний цикл, затем внутренний. Это удобно, чтобы сделать вложенный список плоским или построить сетку.

Превращаем вложенный список в плоский
# Flatten a list of lists
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [num for row in matrix for num in row]
print(flat) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
# This is equivalent to:
flat2 = []
for row in matrix:
for num in row:
flat2.append(num)
print(flat2) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
Вывод
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Создаём пары координат
# Create coordinate pairs
coords = [(x, y) for x in range(3) for y in range(3)]
print(coords)
# [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
Вывод
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]

Когда включения использовать НЕ стоит

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

Не используйте включения, если: логика сложная и условий несколько, внутри цикла нужны побочные эффекты вроде вывода на экран или включение получается шире примерно 80 символов.

Слишком сложно: трудно читать
result = [x * 2 if x > 0 else -x for x in data
         if x != 0 and x % 3 == 0]
Понятный цикл: сразу видно, что он делает
result = []
for x in data:
    if x != 0 and x % 3 == 0:
        if x > 0:
            result.append(x * 2)
        else:
            result.append(-x)

И никогда не используйте включение только ради побочных эффектов. Если вы вызываете print() или меняете внешнюю переменную, напишите обычный цикл. Включение всегда должно строить новую коллекцию.


Практические упражнения

Пора потренироваться! Эти упражнения охватывают все четыре вида включений, от простых до более сложных приёмов.

Упражнение 1: удвойте каждое число
Напишите код

С помощью спискового включения создайте список doubled, в котором каждое число из данного списка умножено на 2.

Ожидаемый вывод:

[2, 4, 6, 8, 10]
numbers = [1, 2, 3, 4, 5]
# Create doubled using a list comprehension
print(doubled)
Упражнение 2: отберите чётные числа
Напишите код

С помощью спискового включения с условием if создайте список evens, в котором будут только чётные числа из данного списка.

Ожидаемый вывод:

[4, 12, 8, 20, 6]
numbers = [3, 4, 7, 12, 8, 15, 20, 6, 9]
# Create evens using a list comprehension with filtering
print(evens)
Упражнение 3: предскажите вывод (if или if-else)
Угадайте вывод

Внимательно прочитайте код и предскажите, что он выведет. Обратите внимание, где стоит if-else.

Впишите вывод точно в том виде, в каком его напечатает Python.

nums = [1, 2, 3, 4, 5]
result = ['even' if n % 2 == 0 else 'odd' for n in nums]
print(result)
Упражнение 4: напишите включение словаря
Напишите код

С помощью включения словаря создайте словарь name_lengths, который сопоставляет каждому имени его длину.

Ожидаемый вывод:

{'Alice': 5, 'Bob': 3, 'Charlie': 7}
names = ['Alice', 'Bob', 'Charlie']
# Create name_lengths using a dict comprehension
print(name_lengths)
Упражнение 5: уникальные гласные через включение множества
Напишите код

С помощью включения множества найдите все уникальные гласные в данном слове. Сохраните результат в переменной vowels_found, а затем выведите его в виде отсортированного списка.

Ожидаемый вывод:

['a', 'e', 'i', 'o']
word = 'conversation'
# Find unique vowels using a set comprehension
# Print them sorted
Упражнение 6: исправьте синтаксис включения
Исправьте ошибку

Этот код пытается создать список, в котором числа больше 5 помечены как 'high', а остальные как 'low'. Но синтаксис неверный, и код падает с ошибкой. Исправьте его.

Ожидаемый вывод:

['low', 'high', 'low', 'high', 'low', 'high']
nums = [3, 8, 2, 9, 1, 7]
result = ['high' for n in nums if n > 5 else 'low']
print(result)
Упражнение 7: перепишите цикл как включение
Улучшите код

Перепишите этот цикл в виде одного спискового включения. Вывод должен остаться точно таким же.

Ожидаемый вывод:

[1, 9, 25, 49, 81]
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
result = []
for n in numbers:
if n % 2 != 0:
result.append(n ** 2)
print(result)
Упражнение 8: сделайте матрицу плоской
Напишите код

С помощью спискового включения с двумя частями for превратите данную матрицу (список списков) в один плоский список. Затем выведите сумму всех элементов с помощью генераторного выражения внутри sum().

Ожидаемый вывод:

[1, 2, 3, 4, 5, 6, 7, 8, 9]
45
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
# Flatten using a list comprehension with nested for clauses
# Print the flat list
# Print the sum using a generator expression

Итоги: что вы узнали о включениях в Python

Вот краткий обзор всего, что вы теперь знаете:

ВидСинтаксисЧто создаёт
Списковое включение[expr for x in items]Список
Список с фильтром[expr for x in items if cond]Отфильтрованный список
Список с преобразованием[a if cond else b for x in items]Преобразованный список
Включение словаря{k: v for x in items}Словарь
Включение множества{expr for x in items}Множество (без повторов)
Генераторное выражение(expr for x in items)Ленивый генератор
Вложенное включение[expr for a in X for b in a]Плоский список

Включения относятся к фирменным возможностям Python. Они делают код короче, понятнее (если применять их с умом) и часто быстрее равноценных циклов.

Что дальше?

Теперь, когда вы умеете быстро создавать и преобразовывать коллекции, можно переходить к [кортежам в Python](/python/python-tuples): неизменяемым последовательностям, которые идеально подходят для фиксированных данных вроде координат, записей и значений, возвращаемых функциями.

Вы узнаете, чем полезна неизменяемость, как работают упаковка и распаковка кортежей и когда выбирать кортеж вместо списка.

Похожие уроки

Урок был полезен?