Главная / Блог / Списковое включение в Python

Списковое включение
(List comprehension) в Python

Списковое включение (List comprehension) в Python

  • Дата: 8 сентября 2020
  • Автор: Михаил Макарик
Python, несмотря на свою относительную молодость, постоянно развивается и стремится максимально походить на обычный текст (чтобы не только компьютеру было удобно работать с кодом, но и программисту). Одна из фишек языка, которой комфортно пользоваться – List comprehension. В русском языке есть 2 варианта перевода: списковое включение или генератор списка. Выбирайте тот, что вам удобнее.
Списковое включение – это некий синтаксический сахар, позволяющий упростить генерацию последовательностей (списков, множеств, словарей, генераторов). Никто не заставляет его использовать в своем коде. Но если вы считаете себя (или стремитесь к этому) профессионалом, вы обязаны знать эту конструкцию.

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

1. Способы формирования списков

В языке Python имеется 3 способа создавать (генерировать) списки:
  1. при помощи циклов;
  2. при помощи функции map();
  3. при помощи list comprehension.
Данные варианты взаимозаменяемы и могут встречаться в чужом коде. Следует иметь представление о каждом из них, чтобы не возникало проблем в ходе работы. Рассмотрим эти возможности.

1.1. Использование цикла For

Наиболее удобным является цикл for (тем не менее, никто не мешает применять цикл while, если вам хочется лишней головной боли). Последовательность действий такова:

  1. Предсоздаем пустой список.
  2. Обходим список, в котором требуется произвести ряд преобразований, или осуществляем требуемое количество итераций цикла при помощи функции range().
  3. Добавляем в пустой список новые значения / элементы с помощью метода append().
Пример 1. Генерация списка кубов для чисел от 0 до 9 с использованием функции range()

s = []  # Создаем пустой список
for i in range(10):  # Осуществляем 10 итераций - от 0 до 9
    s.append(i ** 3)  # Добавляем к списку куб каждого числа
print(s)  # [0, 1, 8, 27, 64, 125, 216, 343, 512, 729]
Пример 2. Генерация списка заглавных букв из слова «Парашют»

# Так как строка является итерируемым объектом, то по ней можно пройтись в цикле.
word = 'Парашют'  # Начальное слово
s = []  # Создаем пустой список
for i in word.upper():  # Проходимся по каждой букве в слове
    s.append(i)  # Приводим все буквы к верхнему регистру
print(s)  # ['П', 'А', 'Р', 'А', 'Ш', 'Ю', 'Т']

1.2. Использование функции map()

Этот способ переводит нас в область функционального программирования. Многие начинающие программисты боятся сталкиваться с этой темой. Понимать суть метода нужно, ведь вы можете встретить его в чужом скрипте.

Следует учесть, что map() возвращает не список, а итератор. Чтобы преобразовать его в список, нужно использовать функцию list(). Для получения итератора нужно задать функцию (которая применится к каждому элементу изначального списка) и сам список.

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

Передают функцию без ее вызова (круглые скобки ставить не нужно). Можно и безымянную функцию создать внутри map() при помощи lambda. Рассмотрим примеры, так будет понятнее.
Пример 3. Генерация списка остатков от деления чисел на 5

def mod5(x):      # Создаем функцию, которая возвращает
    return x % 5  # остаток от деления на 5


s = [5, 7, 11, 20]  # Исходный список
print(list(map(mod5, s)))  # [0, 2, 1, 0] - список остатков
Пример 4. Генерация списков целочисленного деления на 7 с использованием лямбда-функции

s = [7, 22, 49, 701]  # Исходный список чисел
n = list(map(lambda x: x // 7, s))  # Новый список целочисленного деления на 7
print(n)  # [1, 3, 7, 100]

1.3. Использование генератора списка

Smartiqa List Comprehension Sintax
List comprehension – элегантный способ создавать списки в стиле языка Python. Стоит один раз освоить – и вы всегда будете им пользоваться, где это уместно.

Общая структура выражения:
Python

новый_список = [«операция» for «элемент списка» in «список»]
Здесь есть 3 элемента:

– операция подразумевает некие действия, которые вы собираетесь применить к каждому элементу списка;
– элемент списка – каждый отдельный объект списка;
– список – последовательность, элементы которой вы планируете подвергнуть операции (это не обязательно должен быть list, подойдет любой итерируемый объект).

Приведем пример.

Имеется начальный список цен на изделия. Сегодня на них дана скидка 10 %. Составим новый list с учетом удешевления стоимости.
Пример 5. Генерация списков цен на товары с учетом скидок

old_prices = [120, 550, 410, 990]  # Исходный список цен на товары в рублях
discount = 0.15  # Скидка в 15 %
new_prices = [int(product * (1 - discount)) for product in old_prices]  # Вычисляем новые цены (без учета копеек)
print(new_prices)  # [102, 467, 348, 841]
Код выглядит компактным и понятным.

2. Причины использовать list comprehension

Списковые включения считаются более «пайтонообразными», нежели циклы или функция map() для генерации списков. В других популярных языках программирования вы не встретите такой конструкции.

Их можно использовать не только в качестве «создателей» списков, но и для фильтрации данных. Зачастую (но не всегда – об этом будет сказано дальше) list comprehension проще понимать и читать в чужом коде (да и в своем, когда вы вернетесь к нему через некоторое время).

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

3. Применение условий при генерировании списков

При наличии некого начального набора данных нам может понадобиться не просто сделать какие-то однотипные операции над элементами, но и отфильтровать их. В циклах для этого применяются условия. Радует то, что условия можно внедрять и в списковые включения. Они не столь очевидны, поэтому попробуем разобраться.

3.1. Условие в конце включения

Python

новый_список = [«операция» for «элемент списка» in «список» if «условие»]
Такой вариант использования условий позволяет отсечь часть элементов итератора. Новый список будет короче первоначального. По сути, к той же конструкции, которая приведена выше, добавляется условие if.
Пример 6. Получение списка из чисел, которые делятся на 11 без остатка

numbers = [121, 544, 111, 99, 77]  # Исходный список чисел
number11 = [num for num in numbers if num % 11 == 0]  # Выбираем только те числа, которые делятся на 11
print(number11)  # [121, 99, 77]

В результате мы получаем только те элементы, которые делятся на 11 без остатка. Следует обратить внимание, что условие может быть только одно (т. е. здесь невозможно использовать elif, else или другие if, как мы могли бы сделать в циклах).

3.2. Условие в начале включения

Если требуется не фильтрация данных по какому-то критерию, а изменение типа операции над элементами последовательности, условия могут использоваться в начале генератора списков.
Общий вид конструкции:
Python

новый_список = [«операция» if «условие» for «элемент списка» in «список»]
В отличие от предыдущего типа условий, здесь оно может дополняться вариантом else (но elif и тут невозможен).

Пример 7. Генерирование списка, в котором будут отмечены английские и неанглийские буквы

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

Приступаем. Из модуля string импортирован объект ascii_letters, в котором содержатся только буквы английского алфавита:
Python

ascii_letters = {str} 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
Можно, конечно, и самим сделать строку со всеми этими символами, но зачем? В Python есть данная реализация. Следует избегать излишних строк кода, если разработчики уже нам помогли.
Пример 7. Генерирование списка, в котором будут отмечены английские и неанглийские буквы

from string import ascii_letters
letters = 'hыtφтrцзqπ'  # набор букв из разных алфавитов

# Разграничиваем буквы на английские и не английские
is_eng = [f'{letter}-ДА' if letter in ascii_letters else f'{letter}-НЕТ' for letter in letters]
print(is_eng)
# ['h-ДА', 'ы-НЕТ', 't-ДА', 'φ-НЕТ', 'т-НЕТ', 'r-ДА', 'ц-НЕТ', 'з-НЕТ', 'q-ДА', 'π-НЕТ']
В итоге мы получили желаемый список.

4. Сложные списковые включения

Циклы могут иметь несколько уровней вложенности, что относится и к генераторам списков. И вот здесь скрывается одна засада, о которой будет сказано в самом конце. А пока рассмотрим несколько примеров.
Пример 8. Приведение «многомерного» объекта к «одномерному»

# Представим список из слов, который мы хотим привести к сплошному списку из букв. 
# Реализация представлена ниже.

words = ['Я', 'изучаю', 'Python']  # Список слов
letters = [letter for word in words for letter in word]  # Двойная итерация: по словам и по буквам
print(letters)  # ['Я', 'и', 'з', 'у', 'ч', 'а', 'ю', 'P', 'y', 't', 'h', 'o', 'n']
Пример 9. Генерация таблицы умножения от 1 до 5

# Списковые включения могут генерировать список списков. 
# Попробуем сформировать таблицу умножения чисел от 1 до 5. 

table = [[x * y for x in range(1, 6)] for y in range(1, 6)]
print(table)
[[1, 2, 3, 4, 5],
 [2, 4, 6, 8, 10],
 [3, 6, 9, 12, 15],
 [4, 8, 12, 16, 20],
 [5, 10, 15, 20, 25]]
Все получилось, но выглядит конструкция страшновато.

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

5. Ограничения на применение списка включений

Хоть генераторы списков и выглядят красивыми, когда небольшие, они начинают становиться громоздкими по мере роста «мерности» и сложности.

Так как мы пишем код для людей, желательно стремиться к его максимальной читабельности. С приведенными в примере 8 и 9 ситуациями не следует частить. Никто вам не запрещает устроить хоть тройное вложение, но, поверьте, через несколько недель вы не сумеете разобраться в написанном, а что уж говорить о других людях.

Также, list comprehension даже теоретически не везде можно применить. Это касается вопроса замены циклов со множеством условий. Например, если бы задача с буквами разных алфавитов стояла иначе (напротив каждой буквы указать язык, к которому она принадлежит: русский, английский или греческий), мы бы никак не смогли воспользоваться генераторами списков.

6. Дополнение

Подобные конструкции позволяют создавать не только списки, но и множества (set comprehension – при помощи «{}»), генераторы (generator expression – при помощи «()»), а также словари (dictionary comprehension – при помощи «{}»). Принцип везде один и тот же.

Для усвоения темы необходимо тренироваться и повторять материал. Спасибо за внимание.

7. Задачи для самостоятельной работы

A
Сгенерировать список нечетных чисел от 70 до 100.
Б
Отфильтровать список имен (должен получиться новый список, в котором длина слов не превышает 5 символов).
Список: ['Валентин', 'Петр', 'Анна', 'Евгений', 'Константин', 'Валерия', 'Юлия'].
В
Сформировать последовательность чисел на основании предоставленного списка по правилу: если число меньше 5, то удвоить его, а если больше или равно, то отнять от него 2.
Список: [2, 77, 12, 3, 0, 112, 4, -987].
Г
Составить одномерный список, в который будут включены только согласные буквы и привести их к верхнему регистру.
Список: ['Оттава', 'Москва', 'Пекин', 'Полоцк', 'Версаль', 'Дели', 'Каир']
8 СЕНТЯБРЯ / 2020
Как вам материал?

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