yield from 功能解析
- 1.基本功能
- 1.1 传统写法(手动迭代)
- 1.2 使用 yield from
- 2.与普通 yield 的区别
- 3.yield from 的底层行为
- 4.关键应用场景
- 场景 1:拼接多个生成器(如 gen_concatenate)
- 场景 2:捕获子生成器的返回值
- 场景 3:协程(Coroutine)中的委托
- 5.为什么在 gen_concatenate 中用 yield from
- 6.与 itertools.chain 的对比
- 7.总结
在博客《迭代器与生成器(四)》的《13.创建数据处理管道》和《14.展开嵌套的序列》中都提及了 yield from
的用法,本篇博客将进行详细的介绍。
yield from
是 Python 中的一个语法(PEP 380),用于简化生成器(generator)中 委托子生成器 的操作。它的核心作用是让一个生成器能够将部分或全部生成逻辑 “委托” 给另一个生成器,从而避免手动编写循环来逐个生成子生成器的值。下面通过对比和示例详细解释。
1.基本功能
yield from
的主要用途是 扁平化嵌套生成器。
假设有一个生成器 A
需要生成另一个生成器 B
的所有值,传统写法需要手动迭代 B
,而 yield from
可以直接委托。
1.1 传统写法(手动迭代)
def generator_A():
for item in generator_B(): # 手动迭代子生成器
yield item
1.2 使用 yield from
def generator_A():
yield from generator_B() # 自动委托给子生成器
2.与普通 yield 的区别
yield
:生成单个值。yield from
:生成另一个生成器的 所有值。
示例对比:
def sub_gen():
yield 1
yield 2
# 普通 yield 生成的是子生成器对象本身
def gen_normal():
yield sub_gen() # 生成的是生成器对象,不是值
# yield from 生成的是子生成器的值
def gen_delegated():
yield from sub_gen() # 生成 1 和 2
print(list(gen_normal())) # 输出: [<generator object sub_gen at 0x...>]
print(list(gen_delegated())) # 输出: [1, 2]
3.yield from 的底层行为
yield from
实际上等价于以下代码:
def yield_from(gen):
for item in gen: # 自动迭代子生成器
yield item
# 处理子生成器的 return 值(如果有)
但它还额外支持以下高级特性:
- 子生成器的返回值:如果子生成器通过
return
返回值,yield from
可以捕获它(见下文示例)。 - 双向通信:子生成器可以通过
.send()
和.throw()
与外部调用者交互。
4.关键应用场景
场景 1:拼接多个生成器(如 gen_concatenate)
def gen_concatenate(iterators):
for it in iterators:
yield from it # 等价于:for item in it: yield item
# 使用示例
list1 = [1, 2]
list2 = [3, 4]
combined = gen_concatenate([list1, list2])
print(list(combined)) # 输出: [1, 2, 3, 4]
场景 2:捕获子生成器的返回值
def sub_gen():
yield 1
yield 2
return "Done"
def delegator():
result = yield from sub_gen()
print(f"子生成器返回: {result}")
list(delegator())
# 输出:
# 子生成器返回: Done
场景 3:协程(Coroutine)中的委托
在异步编程中,yield from
用于委托子协程(Python
3.5
+
3.5+
3.5+ 后被 await
取代):
def async_task():
yield from asyncio.sleep(1) # 模拟异步IO
5.为什么在 gen_concatenate 中用 yield from
- 避免手动循环:如果不用
yield from
,需要写两层循环:def gen_concatenate(iterators): for it in iterators: for item in it: # 手动嵌套循环 yield item
- 清晰性与性能:
yield from
直接委托给 Python 解释器处理,代码更简洁且效率更高。
6.与 itertools.chain 的对比
yield from
的功能类似于 itertools.chain.from_iterable
,但更灵活:
# 等效实现
from itertools import chain
def gen_concatenate(iterators):
yield from chain.from_iterable(iterators)
但 yield from
的优势在于:
- 可读性:直接表明 “委托生成” 的意图。
- 扩展性:方便添加自定义逻辑(如异常处理或日志)。
7.总结
yield from
的作用:将当前生成器的控制权临时交给另一个生成器,并生成其所有值。- 核心用途:
- 拼接多个生成器(扁平化嵌套迭代)。
- 实现协程委托(Python 3.5 之前)。
- 捕获子生成器的返回值。
- 优势:代码简洁、避免手动循环、支持高级生成器特性。