Генераторы коллекций
Иногда возникает необходимость создать новую коллекцию на основе уже существующей. Представьте, у вас есть список чисел, и вам нужно получить новый список, содержащий только квадраты чётных чисел:
numbers = [1, 2, 3, 4, 5]
Один из способов сделать это – перебрать все элементы списка numbers
в цикле for
и добавить в новый список квадраты тех чисел, которые делятся на 2 без остатка:
squared_numbers = []
for n in numbers:
if n % 2 == 0:
squared_numbers.append(n ** 2)
print(squared_numbers)
# Вывод: [4, 16]
Этот код отлично справляется со своей задачей, но в Python существует более компактный способ создания списков, называемый генератором списка (или списочным выражением). Он позволяет создать новый список всего в одну строку кода, делая код чище и лаконичнее.
Генераторы списков
Генератор списка – это мощный инструмент Python, который позволяет создавать новые списки на основе существующих итерируемых объектов (например, списков, кортежей, строк, диапазонов) с применением определенной логики. Общий синтаксис генератора списка выглядит так:
[выражение for элемент in коллекция if условие]
Давайте разберем каждый элемент этой конструкции:
выражение
– операция, которая применяется к каждомуэлементу
изколлекции
. Результат этого выражения добавляется в новый список. Например, для получения квадрата числа выражение выглядит какn ** 2
.элемент
– временная переменная, которая последовательно принимает значение каждого элемента изколлекции
. В нашем примере с числами это будетn
.коллекция
– итерируемый объект, который можно перебрать в циклеfor
. Это может быть список (например,numbers
), кортеж, строка и даже результат работы функцииrange()
.условие
(необязательно) – фильтр, который позволяет выбирать только теэлементы
изколлекции
, которые удовлетворяют определенному логическому выражению. Еслиусловие
истинно, тоэлемент
обрабатывается и добавляется в новый список. Например,if n % 2 == 0
отбирает только чётные числа.
Если условие отсутствует, обрабатываются все элементы.
Теперь давайте перепишем наш пример с созданием списка квадратов чётных чисел, используя генератор списка:
squared_numbers = [n ** 2 for n in numbers if n % 2 == 0]
print(squared_numbers)
# Вывод: [4, 16]
Как видите, код стал значительно короче и легче для восприятия. Он сразу показывает, что для каждого числа n
из списка numbers
мы хотим получить квадрат его числа n ** 2
, если n
является чётным, то есть if n % 2 == 0
.
Примеры использования генераторов списков
Новый список может быть создан на основе любой из встроенных коллекций.
numbers_tuple = (0, 5, 16, 0, 1, 0, 1)
no_zero_numbers = [n for n in numbers_tuple if n != 0]
print(no_zero_numbers)
# Вывод: [5, 16, 1, 1]
Здесь новый список no_zero_numbers
содержит все ненулевые элемента кортежа numbers_tuple
.
Сами элементы коллекции, используемой в списочной выражении, могут быть представлены любым типом данных: строками или даже другими коллекциями:
passwords_set = {"qwerty", "ahif782ls", "12345", "IUkhsjdf87"}
good_passwords = [password for password in passwords_set if len(password) >= 8]
print(good_passwords)
# Вывод: ['ahif782ls', 'IUkhsjdf87']
Здесь в новом списке good_passwords
содержатся только пароли, длиной больше 8 символов.
Для использования словаря в качестве коллекции в списочном выражении следует вспомнить о методах dict.keys()
и dict.values()
, которые позволяют получить перебираемые последовательности ключей и значений:
users_ages = {"dragon99": 23, "dragon2010": 16, "Цветок Лотоса": 34}
users = [username for username in users_ages.keys() if username.startswith("dragon")]
print(users)
# Вывод: ["dragon99", "dragon2010"]
Также мы можем одновременно перебирать последовательность пар (ключ, значение)
, полученную с помощью метода dict.items()
.Например, если у нас есть словарь с пользователями и их возрастом, то мы можем извлечь имена только совершеннолетних пользователей:
users_ages = {"dragon99": 23, "печенька": 16, "Цветок Лотоса": 34}
adult_users = [user for user, age in users_ages.items() if age >= 18]
print(adult_users)
# Вывод: ['dragon99', 'Цветок Лотоса']
Здесь переменная user
принимает все значения ключей, а переменная age
– значений. При этом, как в условии, так и в выражении мы можем использовать обе эти переменные.
Кроме того, источником данных для нового списка может служить последовательность, полученная с функции range()
:
almost_squared_numbers = [x ** 2 - 1 for x in range(5, 10)]
print(almost_squared_numbers)
# Вывод: [24, 35, 48, 63, 80]
Здесь функция range(5, 10)
генерирует последовательность чисел от 5
до 9
, а затем каждое число возводится в квадрат и уменьшается на единицу.
Вложенные генераторы списков
Генераторы списков могут быть вложенными, что позволяет создавать многомерные структуры данных:
Например, выведем на экран список, содержащий все строки таблицы умножения от 1 до 3:
result = [[i * j for j in range(1, 4) ] for i in range(1, 4)]
print(result)
# Вывод: [[1, 2, 3], [2, 4, 6], [3, 6, 9]]
Здесь внешнее списочное выражение создает три списка, каждый из которых содержит результат умножения текущего числа i
на числа от 1
до 3
.
Генератор множества
По аналогии со списками, можно создавать и множества с помощью генераторов множеств. Для этого используются фигурные скобки {}
вместо квадратных []
:
numbers = [1, 2, 2, 3, 3]
numbers_set = {n for n in numbers}
print(numbers_set)
# Вывод: {1, 2, 3}
Генератор множества автоматически удаляет повторяющиеся элементы, что является ключевой особенностью множеств. Так в примере выше были удалены повторяющиеся числа 2
и 3
.
Генераторы словарей
Для создания словарей также существует специальный синтаксис – генератор словаря. Он, как и множество, использует фигурные скобки {}
, но внутри необходимо указывать пару ключ: значение
, разделенных двоеточием:
{выражение_с_ключом: выражение_с_значением for ключ, значение in коллекция if условие}
Очень похоже на списочное выражение, однако вместо одной переменной используется две: для ключа и для значения.
На коллекцию, из которой берутся данные для создания нового словаря, накладываются те же ограничения, что и на коллекцию, которая может быть передана функции dict()
для создания словаря.
Например, создадим словарь из списка кортежей c парами (ключ, значение)
:
students_and_scores = [
("Врубель Михаил Александрович", 100),
("Шагал Марк Захарович", 98),
("Рерих Николай Константинович", 92)
]
students_scores = {student: score for student, score in students_and_scores}
print(students_scores)
# Вывод: {'Врубель Михаил Александрович': 100, 'Шагал Марк Захарович': 98, 'Рерих Николай Константинович': 92}
Такую же последовательность кортежей можно создать из двух коллекций с помощью функции zip()
:
artists = ["Репин Илья Иванович", "Айвазовский Иван Константинович", "Левитан Исаак Ильич"]
arts = ("Бурлаки на Волге", "Черное море", "Золотая осень")
artists_arts = {artist: art for artist, art in zip(artists, arts)}
print(artists_arts)
# Вывод: {'Репин Илья Иванович': 'Бурлаки на Волге', 'Айвазовский Иван Константинович': 'Черное море', 'Левитан Исаак Ильич': 'Золотая осень'}
В каждом из созданных кортежей, из которых генерируется словарь, первый элемент взят из списка artists
, а второй элемент – из кортежа arts
. Поэтому словарь создаётся так же как и в первом примере из списка кортежей.
Также словарь может быть сгенерирован на основе другого словаря:
user_grades = {
'Математика': [4, 5, 3, 5],
'Русский язык': [5, 4, 4, 5],
'Информатика': [5, 4, 5, 4]
}
average_grades = {subject: sum(grades) / len(grades) for subject, grades in user_grades.items()}
print(average_grades)
# Вывод: {'Математика': 4.25, 'Русский язык': 4.5, 'Информатика': 4.5}
Здесь в качестве значения нового словаря используется среднее арифметическое списка значений исходного словаря.
Отсутствие генераторов кортежей
Несмотря на то, что списки и кортежи во многом похожи и отличаются лишь по изменяемости, мы не можем использовать синтаксис списочного выражения для создания кортежа, просто указав круглые скобки вместо прямоугольных, как мы делали с фигурными скобками для множества и словаря.
Например, попробуем создать кортеж арифметических корней списка целых чисел, используя списочное выражение:
numbers = [1, 4, 9, 16, 25]
root_numbers = (n for n in numbers)
print(root_numbers)
# Вывод: <generator object <genexpr> at 0x7f6bc44a8a00>
Дело в том, что выражение в круглых скобках создает не кортеж, а генератор – особый вид итерируемого объекта, который генерирует значения по запросу, а не хранит их все сразу в памяти. Мы обязательно поговорим о генераторах подробнее в одной из следующих статей.
Просто следует запомнить, что кортеж нельзя сгенерировать с помощью списочного выражения. Однако мы можем сначала создать список, а потом преобразовать его в кортеж с помощью функции tuple()
:
numbers = [1, 4, 9, 16, 25]
root_numbers = tuple([n for n in numbers])
print(root_numbers)
1. Дан список: brands = ["Apple", "Samsung", "Xiaomi", "LG"]
. Напишите программу, которая на его основе создает новый список, содержащий только те слова, длина которых больше или равна 6 символам. Полученный список должен быть выведен на экран.
2. Дан список: numbers = [1, 2, 2, 3, 4, 4, 5]
. Напишите программу, которая на его основе создаёт и выводит на экран множество, содержащее только чётные числа.
3. Дан словарь prices = {"Роза красная": 180.5, "Хризантема жёлтая": 355.5, "Пион розовый": 325.5}
. Напишите программу, которая на его основе создаёт новый словарь, в котором цены будут увеличены на 10%. Полученный словарь должен быть выведен на экран.
4. Напишите программу, которая, используя списочное выражение, создаёт матрицу 3 x 3, заполненную нулями. Полученная матрица должен быть выведен на экран, причём каждый вложенный список должен выводиться на новой строке.
5. Напишите программу, которая запрашивает у пользователя число n
и выводит на экран список квадратов первых n
натуральных чисел.
Пример входных данных | Пример выходных данных |
---|---|
4 | [1, 4, 9, 16] |
6 | [1, 4, 9, 16, 25, 36] |
8 | [1, 4, 9, 16, 25, 36, 49, 64] |