У каждого языка программирования есть свои особенности и преимущества. Одна из культовых фишек 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()
.
nums = [n for n in range(1,6)]
print(nums)
Результат
[1, 2, 3, 4, 5]
В этом примере каждое значение диапазона присваивается переменной n
. Каждое значение возвращается неизменным и добавляется в новый список. Это — та самая n
перед циклом for.
В качестве итерируемого объекта не обязательно должна быть функция range()
. Это может быть любое итерируемое значение
List comprehension с изменением
Теперь пойдем чуть дальше и добавим изменение для каждого значения в цикле.
nums = [1, 2, 3, 4, 5]
squares = [n*n for n in nums]
print(squares)
Результат
[1, 4, 9, 16, 25]
В этом примере два изменения по сравнению с прошлым кодом. Во-первых, в качестве источника используется уже существующий список.
Во-вторых, list comprehension создает список, где каждое значение — это возведенное в квадрат значения оригинального списка.
List comprehension с if
Теперь добавим проверку с помощью if, чтобы не добавлять все значения.
nums = [1, 2, 3, 4, 5]
odd_squares = [n*n for n in nums if n%2 == 1]
print(odd_squares)
Результат
[1, 9, 25]
Инструкция if идет после цикла — в данном случае порядок играет роль.
List comprehension с вложенным циклом for
В последнем примере рассмотрим пример со вложенным циклом for.
matrix = [[x for x in range(1, 4)] for y in range(1, 4)]
print(matrix)
Результат
[[1, 2, 3], [1, 2, 3], [1, 2, 3]]
Может показаться, что здесь все стало чуть сложнее. Но достаточно разбить код на несколько строк, чтобы увидеть, что нет ничего особенного.
matrix = [
[x for x in range(1, 4)]
for y in range(1, 4)
]
print(matrix)
Последний пример. Создадим список дней рождения из списка словарей. Для этого используем знакомые тактики.
people = [{
"first_name": "Василий",
"last_name": "Марков",
"birthday": "9/25/1984"
}, {
"first_name": "Регина",
"last_name": "Павленко",
"birthday": "8/21/1995"
}]
birthdays = [
person[term]
for person in people
for term in person
if term == "birthday"
]
print(birthdays)
В этом примере мы сперва перебираем people
, присваивая каждый словарь person
. После этого перебираем каждый идентификатор в словаре, присваивая ключи term
.
Если значение term
равно birthday
, то значение person[term]
добавляет в list comprehension.
['9/25/1984', '8/21/1995']
Теперь можете попробовать поработать с list comprehension на собственных примерах. Это сделает код более быстрым и компактным.
Представьте, что нам нужно создать список, состоящий из квадратов чисел, определенных от 0 до N-1. Используя текущие знания, это можно было бы реализовать так:
A=[] N=10 for x in range(N): A.append(x**2) print(A)
А теперь этот же алгоритм с использованием list comprehensions:
N=10 A = [x**2 for x in range(N)] print(A)
Смотрите, все создание списка записано в одну строчку. И, кроме того, этот вариант будет работать быстрее предыдущей программы, т.к. Python оптимизирует работу таких конструкций.
Давайте теперь разберемся в этом синтаксисе. Вначале мы указываем, что будем делать с переменной x. Казалось бы, переменная нигде не задана, а мы уже говорим что с ней делать. Да, это так, это такой элемент синтаксиса list comprehensions. Далее, через пробел мы записываем цикл for и уже там указываем эту переменную x и говорим как она будет меняться. То есть, эта временная переменная x существует только внутри списка и пропадает после его создания.
Теперь немного усложним этот пример, и предположим, что нам нужно создать список только из четных чисел. Опять же, обычным способом мы могли бы это сделать так:
A=[] N=10 for x in range(N): if x%2 == 0: A.append(x**2) print(A)
А через list comprehensions это записывается следующим образом:
N=10 A = [x**2 for x in range(N) if x%2 == 0] print(A)
То есть, после цикла мы можем записать любое условие и если оно становится истинным, то формируется новый элемент списка. Видите, как это просто можно реализовать.
Разумеется, вместо возведения в степень мы можем прописывать любые функциональные выражения, или записать просто x:
A = [x for x in range(N) if x%2 == 0]
Тогда получим последовательность четных чисел. Или же формулу вычисления значений линейной функции:
A = [0.5*x+1 for x in range(N)]
И так далее, здесь может быть любое выражение. Также можно оперировать не только числами, но и другими типами данных, например, строками:
cities = ["Москва", "Тверь", "Рязань", "Ярославль", "Владимир"] A = [city for city in cities if len(city) < 7] print(A)
Здесь мы формируем список из названий городов, длины которых меньше семи символов. По этому принципу можно строить самые разные алгоритмы формирования списков.
Генератор списков в Python (подробнее)
Несмотря на столь серьезное название, генератор списков в Python представляет собой не что иное, как простой и короткий способ создания списков. Этот элегантный способ был создан для того, чтобы упростить жизнь программистам, избавив от лишней «писанины».
Давайте рассмотрим простой пример создания списка целых чисел от 0 до 10 двумя способами: стандартным, и с помощью генератора списков
Cоздание списка целых чисел от 0 до 10
1. Стандартный способ:
list=[]fori inrange(10):
list.append(i)# вывод на экран:
print(list)
# результат на экране: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
2. Используем генератор списков:
list = [i for i in range(10)]# вывод на экран:
print(list)
# результат на экране: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Итоговые результаты обоих способов одинаковы, однако, конструкция с генератором списков выглядит более лаконичной (1 строка против 3-х!).
Рассмотрим подробнее, что же представляет собой конструкция генератора списков. В рассмотренном выше примере эта конструкция заключена в квадратные скобки и выглядит как [ i for i in range(10) ].
Квадратные скобки указывают на создаваемый в Python список.
А запись внутри скобок можно расшифровать как: [добавить i для i из промежутка целых чисел: (0, 10)].
Таким образом, обобщенная запись генератора списка будет выглядеть как:
new_list = [ добавить {имя_элемента} для {имя_элемента}, удовлетворяющего условиям {условия}]
В предыдущем примере мы рассматривали создание простейшего списка. Давайте рассмотрим задачку посложнее и создадим список натуральных чисел от 1 до 10, делящихся на 3 без остатка:
Создание списка натуральных чисел, кратных трем:
Так как ноль не относится к натуральным числам, то в данном примере будем рассматривать промежуток (1, 10)
1. Обычный способ:
a
=
[]
for
i
in
range
(
1
,
10
):
if
i
%
3
=
=
0
:
a.append(i)
# вывод на экран
print
(a)
# результат на экране [3, 6, 9]
2. Используем конструкцию генератора списков:
a
=
[i
for
i
in
range
(
10
)
if
i
%
3
=
=
0
]
# вывод на экран
print
(a)
# результат на экране [3, 6, 9]
В данном примере конструкция генератора списков позволила сократить код на 3 строки!
Замена символов в текстовых списках обычным способом и используя генератор списков в Python
Рассмотрим еще один пример удачного применения генератора списков. На этот раз в качестве исходных данных будет список text_list со словами. Нужно заменить в словах списка букву «к» на «т».
1. Стандартный способ:
text_list
=
[
"кот"
,
"ком"
,
"кем"
]
new_text_list
=
[]
for
word
in
text_list:
word
=
word.replace(
"к"
,
"т"
)
new_text_list.append(word)
text_list
=
new_text_list
print
(text_list)
# результат на экране: ['тот', 'том', 'тем']
2. При помощи конструкции генератора списков:
text_list
=
[
"кот"
,
"ком"
,
"кем"
]
text_list
=
[word.replace(
"к"
,
"т"
)
for
word
in
text_list]
print(text_list)
# результат на экране: ['тот', 'том', 'тем']