Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Инструкция 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 — это элегантный способ работать с генераторами и делегировать работу, делая код более читаемым и эффективным.