Баннер мобильный (3) Пройти тест

Генераторы в Python: что это такое, зачем нужны, как их создавать и использовать

Генерируем все самое нужное!

Разбор

8 сентября 2025

Поделиться

Скопировано
Генераторы в Python: что это такое, зачем нужны, как их создавать и использовать

Содержание

    Узнаем, что такое генераторы в языке программирования Python и как их создавать. Рассмотрим несколько примеров практического применения генераторов.

    Что такое генераторы и для чего они нужны

    В разработке ПО программистам приходится иметь дело с большими объемами данных. В этом случае важно не загружать все данные в память сразу, а делать это постепенно. Для этого и придумали генераторы.

    Обычная функция в языке Python завершает работу и возвращает результат сразу после вызова оператора return. В генераторах вместо него используется оператор yield. Когда интерпретатор доходит до этого оператора, то выполнение генератора приостанавливается и сохраняется его текущее состояние. А при следующем вызове генератора выполнение продолжится ровно с того же места, где остановилось.

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

    Как создавать генераторы

    Как вывести в консоль последовательность чисел от 1 до 4? Это можно сделать при помощи простейшего цикла for:

    for i in range(1, 5):
        print(i)

    В консоли получим:

    1
    2
    3
    4

    А как получить такую же последовательность, но с помощью генератора? Для начала надо создать сам генератор. В функции обязательно должен присутствовать оператор yield, при помощи которого генератор выдает значения по очереди. В нашем случае этих yield будет целых четыре:

    def numbers_sequence_generator():
        yield 1
        yield 2
        yield 3
        yield 4

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

    gen = numbers_sequence_generator()

    Теперь четыре раза выведем в консоль эту переменную с помощью print:

    print(next(gen))
    print(next(gen))
    print(next(gen))
    print(next(gen))

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

    for i in gen:
        print(i)

    В итоге получим все ту же последовательность:

    1
    2
    3
    4

    Если мы попытаемся вызвать генератор еще раз, то получим такую ошибку:

    Traceback (most recent call last):
      File "/home/alex/OpenideProjects/generators/generators.py", line 13, in <module>
        print(next(gen))
              ^^^^^^^^^
    StopIteration

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

    gen = [n * n for n in range(10)]
    
    print(gen)

    Вывод:

    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

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

    gen = (n * n for n in range(10))

    Если мы попробуем вывести переменную gen с помощью print(gen), как в прошлом примере, то получим такой вывод:

    <generator object <genexpr> at 0x7e626e481150>

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

    for i in gen:
        print(i)

    В результате получим:

    0
    1
    4
    9
    16
    25
    36
    49
    64
    81

    Практические применения генераторов

    Рассмотрим несколько примеров применения генераторов на практике. Двигаться будем от самых простых примеров до более сложных. 

    Получение числового ряда

    В прошлом разделе мы рассматривали пример генератора с несколькими yield, выводящего числа от 1 до 4. Для наглядного представления работы простейшего генератора это, конечно, неплохой пример, но давайте оставим в коде только один yield. Как это сделать? Очевидно, что здесь надо применить цикл for в теле самого генератора и еще один для вызовов генератора:

    def numbers_sequence_generator(max):
        for i in range(1, max + 1):
            yield i
    
    for number in numbers_sequence_generator(4):
        print(number)

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

    1
    2
    3
    4

    Числа Фибоначчи

    Элементы числовой последовательности, в которой первые два числа равны 0 и 1, а каждое последующее число равно сумме двух предыдущих чисел, называются числами Фибоначчи. Вот пример такой числовой последовательности:

    0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, ...

    Давайте получим первые 10 чисел этого ряда при помощи генератора. Сначала объявим две переменные a и b и присвоим им начальные значения 0 и 1. Далее в цикле while с условием True будем через yield выдавать значение переменной a. В последней строчке генератора применим формулу для получения следующих значений. Все это будет крутиться в цикле до тех пор, пока генератор не будет остановлен. Вот такой генератор у нас должен получиться:

    def fibonacci_numbers():
        a, b = 0, 1
        while True:
            yield a
            a, b = b, a + b

    Теперь объявим переменную, в которую будут записываться поочередно выдаваемые генератором значения:

    fib_num = fibonacci_numbers()

    Ну, и последним делом добавим цикл, в котором пропишем ограничение вызовов генератора и вывод значений в консоль:

    for _ in range(10):
        print(next(fib_num))

    После запуска в консоли получим такой вывод:

    0
    1
    1
    2
    3
    5
    8
    13
    21
    34

    Чтение текстового файла

    Генераторы могут помочь с чтением текстовых файлов. Чтобы прочитать файл, сначала нужно его открыть. Для этого нам понадобится функция open. В качестве параметров для этой функции надо указать путь до файла, режим чтения и кодировку текста. Режим чтения укажем как r. Это означает, что файл открывается только для чтения. Кодировку ставим utf-8. 

    Само чтение будет происходить в цикле for, в теле которого каждая строка файла возвращается посредством оператора yield, попутно избавляясь от лишних пробелов на концах при помощи функции strip. В общем, код генератора у нас получается такой:

    def read_file(filename):
        with open(filename, "r", encoding="utf-8") as file:
            for line in file:
                yield line.strip()

    В качестве текстового файла возьмем, к примеру, отрывок из романа в стихах «Евгений Онегин» Александра Сергеевича Пушкина. Назовем этот файл onegin.txt и положим его рядом с нашим исходником. Указываем этот файл в виде параметра для нашего генератора и делаем это в цикле, в котором будет вызываться генератор:

    for read_line in read_file("onegin.txt"):
        print(read_line)

    Вывод в консоли:

    Кого ж любить? Кому же верить?
    Кто не изменит нам один?
    Кто все дела, все речи мерит
    Услужливо на наш аршин?
    Кто клеветы про нас не сеет?
    Кто нас заботливо лелеет?
    Кому порок наш не беда?
    Кто не наскучит никогда?
    Призрака суетный искатель,
    Трудов напрасно не губя,
    Любите самого себя,
    Достопочтенный мой читатель!
    Предмет достойный: ничего
    Любезней, верно, нет его.

    Мы показали здесь совсем небольшой текстовый файл, но с помощью генераторов можно легко прочитать хоть всего Евгения Онегина.

    Генератор паролей

    Для создания паролей, состоящих из разных наборов символов, нам потребуется импортировать два модуля. Это модули random и string. Модуль random нам понадобился из-за содержащейся в нем функции choices, которая умеет перестанавливать элементы в случайном порядке. А модуль string полезен тем, что в нем есть все необходимые нам константы с буквами, числами и прочим. Импортируем модули:

    import random
    import string

    Сам генератор включает в себя цикл while с условием True, а в теле этого цикла находится оператор yield, который и возвращает нам случайный пароль:

    def random_password_generator(length=16):
        while True:
            yield ''.join(random.choices(string.ascii_letters + string.digits + string.punctuation, k=length))

    Как видно, пароль будет иметь длину в 16 символов и состоять из букв разного регистра (string.ascii_letters), чисел (string.digits) и различных пунктуационных символов (string.punctuation). Далее объявляем переменную password:

    password = random_password_generator()

    И на последнем этапе с помощью функции print выведем три варианта пароля:

    print(next(password))
    print(next(password))
    print(next(password))

    Пример вывода в консоли:

    R#6j26L!w6Zsbtw|
    cwrR]%H:\jq1]a&/
    Ijs|9>(wS125[$4D

    Пароли выглядят довольно надежными!

    Генерация простых чисел

    Простые числа — это такие натуральные числа больше единицы, которые делятся нацело только на единицу и на самих себя. Понятно, что самое первое простое число — это 2. Поэтому в самом начале генераторной функции инициализируем переменную number значением 2. Также нам понадобится лимит, указывающий, до какого числа генерировать простые числа. Этот лимит прописываем в цикле while в качестве условия. В теле цикла разместим условный оператор if. Он будет проверять числа на простоту. Если условие оператора окажется истинным, то при помощи yield вернется простое число. После отработки оператора if переменная number увеличивается на единицу и цикл повторяется снова и снова, пока истинно его условие. Код генератора:

    def prime_numbers_generator(limit):
        number = 2
        while number < limit:
            if all(number % i != 0 for i in range(2, int(number ** 0.5) + 1)):
                yield number
            number += 1

    Метод all в условии оператора if возвращает True, если все элементы итерации в составе его параметра оказываются истинными. В противном случае возвращается False. Вывод простых чисел происходит посредством цикла for:

    for prime_number in prime_numbers_generator(50):
        print(prime_number)

    Мы указали значение лимита, равное 50. Значит, должны получиться простые числа меньшие этого значения. Вывод в консоли будет таким:

    2
    3
    5
    7
    11
    13
    17
    19
    23
    29
    31
    37
    41
    43
    47

    Поиск слова в тексте

    Для поиска слова в тексте будем так же, как и в других случаях, применять в генераторе цикл while с условием True. В теле цикла будет вычисляться позиция искомого слова в тексте при помощи метода find. На вход этого метода подаются искомое слово и его начальная позиция, а сам метод вызываем для текста, в котором ищем слово. Перед тем как запускать цикл с этим методом, надо перевести текст и искомое слово в нижний регистр. Делаем это при помощи метода lower. Код генератора:

    def search_word(text, search_term):
        text_lower = text.lower()
        term_lower = search_term.lower()
    
        position = 0
    
        while True:
            position = text_lower.find(term_lower, position)
    
            if position == -1:
                break
    
            yield position
    
            position += len(term_lower)

    Перед циклом позиция получает нулевое значение. Если позиция оказывается равна -1, то происходит выход из цикла. Для примера возьмем следующий текст:

    input_text = """
        Python - это мощный язык программирования.
        Он позволяет легко работать с текстом и данными.
        Генераторы делают код более эффективным!
        Python используется во многих проектах.
        """

    Искать будем слово «python»:

    word_to_find = "python"

    Создаем переменную для генератора:

    generator = search_word(input_text, word_to_find)

    Выводим сообщение о том, какое слово ищем:

    print(f"Поиск слова '{word_to_find}':")

    И запускаем цикл с вызовами генератора:

    for word_position in generator:
        print(f"Найдено на позиции: {word_position}")

    Результат в консоли:

    Поиск слова 'python':
    Найдено на позиции: 5
    Найдено на позиции: 151

    Далее предлагаем читателю выполнить небольшое задание.

    Задание для самостоятельного выполнения

    Напишите программу, которая выводит в консоль кубы натуральных чисел от 1 до 6. Программа должна быть в двух вариантах: один вариант производит вычисления при помощи генератора, а другой — при помощи генераторного выражения.

    Решение:

    Для варианта с генератором будем использовать цикл for:

    def cube_generator():
        for i in range(1, 7):
            yield i ** 3
    
    gen = cube_generator()
    
    for cube in gen:
        print(cube)

    Вариант с генераторным выражением:

    cubes = (i ** 3 for i in range(1, 7))
    
    for cube in cubes:
        print(cube)

    И в том и в другом случае вывод в консоли будет таким:

    1
    8
    27
    64
    125
    216

    (Конец решения)

    Разбор

    Поделиться

    Скопировано
    0 комментариев
    Комментарии