Что такое функция-генератор в Python?

Ответ

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

Ключевые особенности:

  • Ленивые вычисления: Элементы генерируются по требованию, что экономит память при работе с большими последовательностями.
  • Сохранение состояния: Локальные переменные функции сохраняются между вызовами.
  • Итерационный протокол: Генераторы автоматически реализуют методы __iter__() и __next__().

Пример:

def countdown(n):
    """Генератор, который отсчитывает от n до 1."""
    while n > 0:
        yield n
        n -= 1

# Использование
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

Практическое применение:

  1. Обработка больших файлов: Построчное чтение без загрузки всего файла в память.
  2. Генерация бесконечных последовательностей: Например, поток данных.
  3. Конвейерная обработка: Создание цепочек генераторов для эффективной обработки данных.

Сравнение с обычной функцией:

# Обычная функция (возвращает весь список)
def get_squares_list(n):
    return [i**2 for i in range(n)]  # Память O(n)

# Генератор (генерирует значения по одному)
def get_squares_gen(n):
    for i in range(n):
        yield i**2  # Память O(1)

Ответ 18+ 🔞

Смотри, вот тебе про генераторы в Питоне, но без этой сухой академической хуйни. Представь, что обычная функция — это как работяга, который притаскивает тебе сразу целый поддон кирпичей, ебать, и говорит: «Держи, мучайся». А генератор — это хитрая жопа, которая подносит тебе по одному кирпичику, как только ты его попросил. И пока ты не попросил следующий, она стоит, курит и ждёт. Экономия места, понимаешь? Не надо склад строить.

В чём прикол, собственно?

  • Ленивость, мать её: Он нихуя не делает, пока ты не скажешь «дай». Идеально, когда данных овердохуища, а память одна.
  • Помнит всё: Он как слон, блядь. Запомнил, на каком yield остановился, и с этого места продолжает.
  • Сам умный: Сам в себе реализует всю эту магию с next() и for, тебе париться не надо.

Смотри, как это выглядит в деле:

def обратный_отсчёт(n):
    """Эта штука будет тикать от n до единицы."""
    while n > 0:
        yield n  # Вот он, волшебный пиздец! Отдаёт n и засыпает.
        n -= 1   # Проснётся тут, когда попросишь следующий элемент.

# Юзаем в цикле, как нормальные люди
for цифра in обратный_отсчёт(5):
    print(цифра)  # Напечатает: 5, потом 4, потом 3... ну ты понял.

# А можно и вручную дёргать, как из автомата
ген = обратный_отсчёт(3)
print(next(ген))  # 3
print(next(ген))  # 2
print(next(ген))  # 1
# print(next(ген))  # А вот тут будет StopIteration, пиздец, кончилось всё.

Где это реально впаривают?

  1. Файлы гигабайтные: Читать построчно, а не загружать весь файл в оперативку и охуевать.
  2. Бесконечные штуки: Типа генератора случайных чисел или стрима данных — ему похуй, он вечный.
  3. Конвейеры: Один генератор фильтрует, второй преобразует, третий выдаёт — красота, а не жизнь.

Чё там с обычными функциями? Сравниваем:

# Обычная функция-работяга: делает всё и сразу
def квадраты_списком(n):
    return [i**2 for i in range(n)]  # Бля, создаст список из всех квадратов! Память O(n).

# Генератор-хитрец: работает по запросу
def квадраты_генератором(n):
    for i in range(n):
        yield i**2  # Выдал один квадрат и отдыхает. Память O(1), ёпта!

Вот и вся магия. Не функция, а мечта ленивого программиста.