- Списковое включение (List comprehension) в Python
- 1. Способы формирования списков
- 1.1. Использование цикла For
- 1.2. Использование функции map()
- Читайте также
- 1.3. Использование генератора списка
- 2. Причины использовать list comprehension
- 3. Применение условий при генерировании списков
- 3.1. Условие в конце включения
- 3.2. Условие в начале включения
- 4. Сложные списковые включения
- Читайте также
- 5. Ограничения на применение списка включений
- Руководство по использованию list comprehension
- Преимущества list comprehension
- Создание первого list comprehension
Списковое включение (List comprehension) в Python
Python, несмотря на свою относительную молодость, постоянно развивается и стремится максимально походить на обычный текст (чтобы не только компьютеру было удобно работать с кодом, но и программисту). Одна из фишек языка, которой комфортно пользоваться – List comprehension. В русском языке есть 2 варианта перевода: списковое включение или генератор списка. Выбирайте тот, что вам удобнее.
Списковое включение – это некий синтаксический сахар, позволяющий упростить генерацию последовательностей (списков, множеств, словарей, генераторов). Никто не заставляет его использовать в своем коде. Но если вы считаете себя (или стремитесь к этому) профессионалом, вы обязаны знать эту конструкцию.
Поначалу list comprehension может показаться сложным, но как только вы освоите эту возможность, то без проблем будете с ней работать.
1. Способы формирования списков
- при помощи циклов;
- при помощи функции map() ;
- при помощи list comprehension.
1.1. Использование цикла For
Наиболее удобным является цикл for (тем не менее, никто не мешает применять цикл while , если вам хочется лишней головной боли). Последовательность действий такова:
- Предсоздаем пустой список.
- Обходим список, в котором требуется произвести ряд преобразований, или осуществляем требуемое количество итераций цикла при помощи функцииrange().
- Добавляем в пустой список новые значения / элементы с помощью метода append() .
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]
# Так как строка является итерируемым объектом, то по ней можно пройтись в цикле. word = 'Парашют' # Начальное слово s = [] # Создаем пустой список for i in word.upper(): # Проходимся по каждой букве в слове s.append(i) # Приводим все буквы к верхнему регистру print(s) # ['П', 'А', 'Р', 'А', 'Ш', 'Ю', 'Т']
1.2. Использование функции map()
Этот способ переводит нас в область функционального программирования. Многие начинающие программисты боятся сталкиваться с этой темой. Понимать суть метода нужно, ведь вы можете встретить его в чужом скрипте.
Следует учесть, что map() возвращает не список, а итератор. Чтобы преобразовать его в список, нужно использовать функцию list() . Для получения итератора нужно задать функцию (которая применится к каждому элементу изначального списка) и сам список.
Читайте также
Рассказываем, что такое лямбда функции и зачем они нужны, как их создать и использовать внутри как обычных функций, так и служебных: filter, map, reduce
Передают функцию без ее вызова (круглые скобки ставить не нужно). Можно и безымянную функцию создать внутри map() при помощи lambda . Рассмотрим примеры, так будет понятнее.
def mod5(x): # Создаем функцию, которая возвращает return x % 5 # остаток от деления на 5 s = [5, 7, 11, 20] # Исходный список print(list(map(mod5, s))) # [0, 2, 1, 0] - список остатков
s = [7, 22, 49, 701] # Исходный список чисел n = list(map(lambda x: x // 7, s)) # Новый список целочисленного деления на 7 print(n) # [1, 3, 7, 100]
1.3. Использование генератора списка
List comprehension – элегантный способ создавать списки в стиле языка Python. Стоит один раз освоить – и вы всегда будете им пользоваться, где это уместно.
Общая структура выражения:
новый_список = [«операция» for «элемент списка» in «список»]
– операция подразумевает некие действия, которые вы собираетесь применить к каждому элементу списка;
– элемент списка – каждый отдельный объект списка;
– список – последовательность, элементы которой вы планируете подвергнуть операции (это не обязательно должен быть list , подойдет любой итерируемый объект).
Имеется начальный список цен на изделия. Сегодня на них дана скидка 10 %. Составим новый l ist с учетом удешевления стоимости.
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. Условие в конце включения
новый_список = [«операция» for «элемент списка» in «список» if «условие»]
Такой вариант использования условий позволяет отсечь часть элементов итератора. Новый список будет короче первоначального. По сути, к той же конструкции, которая приведена выше, добавляется условие if .
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. Условие в начале включения
Если требуется не фильтрация данных по какому-то критерию, а изменение типа операции над элементами последовательности, условия могут использоваться в начале генератора списков.
Общий вид конструкции:
новый_список = [«операция» if «условие» for «элемент списка» in «список»]
В отличие от предыдущего типа условий, здесь оно может дополняться вариантом else (но elif и тут невозможен).
Пример 7. Генерирование списка, в котором будут отмечены английские и неанглийские буквы
Представим задачу: нам дают строку, в которой могут присутствовать буквы любых алфавитов. И вот мы захотели составить новый список, где напротив каждой буквы будет отмечено, является ли она английской или нет.
Приступаем. Из модуля string импортирован объект ascii_letters , в котором содержатся только буквы английского алфавита:
ascii_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
Можно, конечно, и самим сделать строку со всеми этими символами, но зачем? В Python есть данная реализация. Следует избегать излишних строк кода, если разработчики уже нам помогли.
from string import ascii_letters letters = 'hыtφтrцзqπ' # набор букв из разных алфавитов # Разграничиваем буквы на английские и не английские is_eng = [f'-ДА' if letter in ascii_letters else f'-НЕТ' for letter in letters] print(is_eng) # ['h-ДА', 'ы-НЕТ', 't-ДА', 'φ-НЕТ', 'т-НЕТ', 'r-ДА', 'ц-НЕТ', 'з-НЕТ', 'q-ДА', 'π-НЕТ']
4. Сложные списковые включения
Циклы могут иметь несколько уровней вложенности, что относится и к генераторам списков. И вот здесь скрывается одна засада, о которой будет сказано в самом конце. А пока рассмотрим несколько примеров.
# Представим список из слов, который мы хотим привести к сплошному списку из букв. # Реализация представлена ниже. words = ['Я', 'изучаю', 'Python'] # Список слов letters = [letter for word in words for letter in word] # Двойная итерация: по словам и по буквам print(letters) # ['Я', 'и', 'з', 'у', 'ч', 'а', 'ю', 'P', 'y', 't', 'h', 'o', 'n']
# Списковые включения могут генерировать список списков. # Попробуем сформировать таблицу умножения чисел от 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]]
Читайте также
Разбираем типы данных в Python: списки, кортежи, словари, множества и т. д. Рассматриваем часто используемые способы ввода-вывода данных. Решаем задачи.
5. Ограничения на применение списка включений
Хоть генераторы списков и выглядят красивыми, когда небольшие, они начинают становиться громоздкими по мере роста «мерности» и сложности.
Так как мы пишем код для людей, желательно стремиться к его максимальной читабельности. С приведенными в примере 8 и 9 ситуациями не следует частить. Никто вам не запрещает устроить хоть тройное вложение, но, поверьте, через несколько недель вы не сумеете разобраться в написанном, а что уж говорить о других людях.
Также, list comprehension даже теоретически не везде можно применить. Это касается вопроса замены циклов со множеством условий. Например, если бы задача с буквами разных алфавитов стояла иначе (напротив каждой буквы указать язык, к которому она принадлежит: русский, английский или греческий), мы бы никак не смогли воспользоваться генераторами списков.
Руководство по использованию list comprehension
У каждого языка программирования есть свои особенности и преимущества. Одна из культовых фишек Python — list comprehension (редко переводится на русский, но можно использовать определение «генератора списка»). Comprehension легко читать, и их используют как начинающие, так и опытные разработчики.
List comprehension — это упрощенный подход к созданию списка, который задействует цикл for, а также инструкции if-else для определения того, что в итоге окажется в финальном списке.
Преимущества list comprehension
У list comprehension есть три основных преимущества.
- Простота. List comprehension позволяют избавиться от циклов for, а также делают код более понятным. В JavaScript, например, есть нечто похожее в виде map() и filter() , но новичками они воспринимаются сложнее.
- Скорость. List comprehension быстрее for-циклов, которые он и заменяет. Это один из первых пунктов при рефакторинге Python-кода.
- Принципы функционального программирования. Это не так важно для начинающих, но функциональное программирование — это подход, при котором изменяемые данные не меняются. Поскольку list comprehensions создают новый список, не меняя существующий, их можно отнести к функциональному программированию.
Создание первого list comprehension
List comprehension записывается в квадратных скобках и задействует цикл for. В процессе создается новый список, куда добавляются все элементы оригинального. По мере добавления элементов их можно изменять.
Для начала возьмем простейший пример: создадим список из цифр от 1 до 5, используя функцию range() .