#2 Генераторы могут получать значения с помощью метода send().

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

def first_ten_numbers():
    for i in range(10):
        yield i

def square_numbers(numbers):
    for num in numbers:
        yield num * num

squared = square_numbers(first_ten_numbers())
print(list(squared))  # Output: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Использование send()
Генераторы могут получать значения с помощью метода send(). Этот трюк позволяет динамически контролировать поведение генератора.

def repeater():
    value = None
    while True:
        received = yield value
        if received is not None:
            value = received

rep = repeater()
next(rep)
print(rep.send("Hello"))  # Output: Hello
print(rep.send("World"))  # Output: World

Генераторы с itertools
Модуль itertools содержит функции, возвращающие итераторы. Объедините их с генераторами для получения мощных результатов.

import itertools

def limited_cycle(iterable, limit):
    infinite = itertools.cycle(iterable)
    for _ in range(limit):
        yield next(infinite)

print(list(limited_cycle("AB", 5)))  # Output: ['A', 'B', 'A', 'B', 'A']

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

def grep(pattern):
    print(f"Looking for {pattern}")
    while True:
        line = (yield)
        if pattern in line:
            print(line)

search = grep("Python")
next(search)
search.send("I love Python!")

Бесконечные генераторы
Генераторы могут создавать бесконечные последовательности. Используйте их с умом, чтобы избежать бесконечных циклов.

def infinite_ones():
    while True:
        yield 1

# Use islice to get a finite sequence from an infinite generator
finite_ones = itertools.islice(infinite_ones(), 5)
print(list(finite_ones))  # Output: [1, 1, 1, 1, 1]