Что такое генератор (generator) в Python?

Ответ

Генератор в Python — это специальный итератор, который позволяет лениво (по требованию) генерировать значения, не храня всю последовательность в памяти одновременно. Это делает их исключительно эффективными для работы с большими или бесконечными потоками данных.

Два основных способа создания:

  1. Функция-генератор (Generator function): Использует ключевое слово yield вместо return. При вызове такая функция возвращает объект-генератор, а её выполнение приостанавливается после каждого yield и возобновляется при следующем вызове next().

    def countdown(n):
        """Генератор, который отсчитывает от n до 1."""
        while n > 0:
            yield n  # Возвращает значение и приостанавливается
            n -= 1
        # По достижении конца функция неявно вызывает StopIteration
    
    # Использование
    for number in countdown(5):
        print(number)  # 5, 4, 3, 2, 1
    
    # Или вручную
    gen = countdown(3)
    print(next(gen))  # 3
    print(next(gen))  # 2
    print(next(gen))  # 1
    # print(next(gen))  # Вызовет StopIteration
  2. Генераторное выражение (Generator expression): Похоже на list comprehension, но использует круглые скобки и ленивые вычисления.

    # List comprehension (сразу создаёт список в памяти)
    squares_list = [x**2 for x in range(1000000)]  # Занимает много памяти
    
    # Generator expression (генерирует значения на лету)
    squares_gen = (x**2 for x in range(1000000))  # Практически не занимает памяти
    print(next(squares_gen))  # 0
    print(next(squares_gen))  # 1

Ключевые преимущества:

  • Экономия памяти: значения вычисляются и возвращаются по одному.
  • Ленивые вычисления: генератор не начинает работу, пока не запрошено первое значение.
  • Работа с бесконечными последовательностями:
    def infinite_counter():
        i = 0
        while True:
            yield i
            i += 1

Под капотом: Генераторы реализуют протокол итератора (методы __iter__() и __next__()), а также имеют методы .send(value), .throw(exception) и .close() для более сложного взаимодействия.

Ответ 18+ 🔞

А, генераторы в Python! Ну это, блядь, одна из тех штук, которая сначала кажется какой-то мартышлюшкой, а потом понимаешь — ёперный театр, как же без них жил-то.

Представь себе, чувак, обычная функция — это как буфет. Зашёл, набрал полную тарелку всего (то есть весь список в памяти), сел жрать. А генератор — это суши-конвейер. Сидишь, и тебе по одной штучке подвозят, когда попросишь. Не надо сразу овердохуища данных в оперативку тащить.

Как их делают, этих хитрожопых:

  1. Функция-генератор. Всё просто: пишешь функцию, но вместо return ставишь yield. И всё — она уже не функция, а генератор, сам от себя охуевший от такой метаморфозы.

    def countdown(n):
        """Генератор, который отсчитывает от n до 1."""
        while n > 0:
            yield n  # Выплюнул значение и уснул до следующего раза
            n -= 1
        # Дошёл до конца — тихо сдох (StopIteration)
    
    # Использование
    for number in countdown(5):
        print(number)  # 5, 4, 3, 2, 1
    
    # Или вручную, как мудак
    gen = countdown(3)
    print(next(gen))  # 3
    print(next(gen))  # 2
    print(next(gen))  # 1
    # print(next(gen))  # А тут тебе в глаз — StopIteration

    Суть в том, что после yield функция замирает, запоминает, где остановилась, и ждёт следующего пинка next(). Красота, я тебя в сраку.

  2. Генераторное выражение. Это вообще чих-пых тебя в сраку, какая элегантная хуйня. Как list comprehension, но в круглых скобках и без жадности.

    # List comprehension — жадный уёбок, сразу всё в память грузит
    squares_list = [x**2 for x in range(1000000)]  # Память кончилась, комп встал
    
    # Generator expression — хитрая жопа, отдаёт по одному
    squares_gen = (x**2 for x in range(1000000))  # Памяти жрёт как кот наплакал
    print(next(squares_gen))  # 0
    print(next(squares_gen))  # 1

А главные плюсы-то какие, ёпта?

  • Память не жрёт. Серьёзно, можно хоть терабайтный файл читать строчку за строчкой, и ничего не взорвётся.
  • Ленивый как Фарлаф. Не начнёт работать, пока не скажешь «дай».
  • Бесконечные штуки делать можно. Вот смотри, ни хуя себе простой пример:

    def infinite_counter():
        i = 0
        while True:
            yield i
            i += 1

    Попробуй такой список в память запихнуть — будет вам хиросима и нигерсраки. А генератору да похуй, он тебе хоть до скончания века числа выдавать будет.

Под капотом там, конечно, магия: методы __iter__, __next__, ещё можно в него значения .send() кидать или исключения .throw(). Но для начала просто запомни: yield — твой друг, когда данных дохуя, а память одна. Всё гениальное просто, как хуй с горы.

Видео-ответы