← Назад к вопросам

Для чего нужна инструкция yield from?

2.0 Middle🔥 141 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Инструкция yield from

yield from — это синтаксическая конструкция, добавленная в Python 3.3, которая позволяет одному генератору делегировать свою работу другому генератору или итерируемому объекту. Это упрощает код и делает его более эффективным.

Базовое применение

def inner_generator():
    yield 1
    yield 2
    yield 3

def outer_generator_old():
    # Старый способ — через явный цикл
    for value in inner_generator():
        yield value

def outer_generator_new():
    # Новый способ — через yield from
    yield from inner_generator()

print(list(outer_generator_new()))  # Output: [1, 2, 3]

Это экономит строки кода и делает намерение ясным: мы полностью делегируем работу другому генератору.

Передача значений в подгенератор

Одно из мощных свойств yield from — это двусторонняя коммуникация. Значения, отправленные в основной генератор через send(), передаются в подгенератор:

def inner():
    print("Inner started")
    result = yield "waiting for value"
    print(f"Inner received: {result}")
    yield "done"

def outer():
    yield from inner()

gen = outer()
print(next(gen))  # Output: Inner started, waiting for value
print(gen.send("hello"))  # Output: Inner received: hello, done

Без yield from пришлось бы вручную пробрасывать send() через цикл.

Обработка исключений

yield from также правильно пробрасывает исключения и обрабатывает return значения подгенератора:

def inner():
    try:
        yield 1
        yield 2
    except ValueError:
        print("Caught ValueError in inner")
        yield 99

def outer():
    yield from inner()

gen = outer()
print(next(gen))  # Output: 1
print(next(gen))  # Output: 2
try:
    gen.throw(ValueError("test"))
except StopIteration:
    pass

Return значения из подгенератора

def inner():
    yield 1
    yield 2
    return "result from inner"

def outer():
    result = yield from inner()
    print(f"Got: {result}")
    yield "final"

gen = outer()
print(list(gen))  # Output: [1, 2, final]

Результат return в подгенераторе становится значением выражения yield from.

Практические примеры

1. Работа с деревом

class Node:
    def __init__(self, value, children=None):
        self.value = value
        self.children = children or []

def traverse_tree(node):
    yield node.value
    for child in node.children:
        yield from traverse_tree(child)

root = Node(1, [
    Node(2, [Node(4), Node(5)]),
    Node(3, [Node(6)])
])

print(list(traverse_tree(root)))  # Output: [1, 2, 4, 5, 3, 6]

2. Объединение нескольких генераторов

def gen_a():
    yield from range(3)

def gen_b():
    yield from "abc"

def combined():
    yield from gen_a()
    yield from gen_b()

print(list(combined()))  # Output: [0, 1, 2, a, b, c]

3. Цепочка обработчиков (паттерн Coroutine)

def processor(name, next_processor=None):
    try:
        while True:
            value = yield
            print(f"{name} processing: {value}")
            if next_processor:
                next_processor.send(value)
    except GeneratorExit:
        print(f"{name} closed")

p2 = processor("P2")
next(p2)
p1 = processor("P1", p2)
next(p1)

p1.send(10)  # Output: P1 processing: 10, P2 processing: 10

Когда использовать yield from

  • Делегирование генерации — когда нужно передать всю работу другому генератору
  • Рекурсивные структуры — обход деревьев, графов
  • Цепочки обработки — в корутинах и асинхронном коде
  • Упрощение кода — вместо явных циклов с yield

В Python 3.5+ yield from также используется в асинхронном коде (хотя теперь есть await):

# Старый синтаксис (Python 3.3-3.4)
@asyncio.coroutine
def old_style():
    result = yield from some_coroutine()

# Новый синтаксис (Python 3.5+)
async def new_style():
    result = await some_coroutine()

yield from — это элегантный способ работать с генераторами и делегировать работу, делая код более читаемым и эффективным.