Генераторы

Хочу поделиться любопытным паттерном.

Многие знают генераторы в Python:

def fibonacci_generator():
    a, b = 0, 1  # Начальные два числа Фибоначчи
    while True:
        yield a  # Возвращаем текущее число
        a, b = b, a + b  # Обновляем числа для следующего шага

# Пример использования генератора:
fib_gen = fibonacci_generator()

# Генерация первых 10 чисел Фибоначчи:
for _ in range(10):
    print(next(fib_gen))

yield возвращет текущее значение из функции, а функция продолжает работать.

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

В этой же статье, подробно останавливаться на основах генераторов не буду.

Отправка данных внутрь генератора

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

Для этого мы присваиваем новой переменной значение, возвращаемое yield:

a = yield

или

a = yield "Generated value"

А чтобы отправить это значение из основного кода, нужно воспользовать методом генератора .send(value).

gen.send(123)

Прежде чем мы сможем отправить первое значение в генератор мы должны дойти до первого yield внутри генератора, то есть нужно один раз проитерироваться: либо next(my_gen), либо my_gen.send(None) Я предпочитаю второй вариант, как более согласованный с другими вызовами.

Вот так это выглядит вместе:

Потестить онлайн

def generator():
    # Первый yield приостанавливает выполнение и возвращает значение 1
    a = yield 1  
    print(f"Got {a}")  # Когда send(10) передает значение, оно будет доступно в переменной a
    # Второй yield возвращает a + 1, где a — это значение, переданное через send
    yield a + 1

# Создание генератора
gen = generator()

# Запуск генератора. send(None) используется для старта генератора,
# так как первый yield требует от него отдачи значения, не передавая данных.
first = gen.send(None)  
print(f"First generated value is {first}")

# Отправляем значение 10 в генератор, что будет присвоено переменной 'a'
second = gen.send(10)  
print(f"Second generated value is {second}")