“yield from”语句是在python3.3版本中引入的,见PEP 380 -"Syntax for Delegating to a Subgenerator",从标题上看使用"yield from"涉及两个生成器。
在"yield from"出现前我们需要使用for循环迭代生成器,像下面这样,
for item in iterable:
yield item
有了"yield from",你可以这样写,
yield from iterable
是不是很简洁,但这仅是其中一个功能,如果"yield from"仅有这一个功能,它可能不会在Python 3.3中被引入。其实引入"yield from"语句最大的作用是在调用者和子生成器之间建立双向的透明通道,依赖这个通道可以向子生成器发送数据并接受返回的结果,调用者还可以通过管道向子生成器传递异常(使用"生成器的throw方法")。
下面通过两个实例展示"yield from"的常见使用场景。在正式开始之前,有几个概念需要说明:
- 委派生成器:包含"yield from <iterable>"表达式的生成器函数
- 子生成器:从"yield from"表达式中<iterable>部分获取的生成器
- 调用方:调用委派生成器的客户端代码
发送数据到生成器
#!/usr/bin/python3
def my_printer():
while True:
w = yield
print('>> ', w)
def my_wrapper(coro):
yield from coro
w = my_printer()
wrap = my_wrapper(w)
wrap.send(None) #启动生成器,不可少
for i in range(4):
wrap.send(i)
my_wrapper即为“委派生成器”,my_printer是上面所说的子生成器,调用者是指10-14行的代码,调用者通过"yield from"产生的透明管道将数据传输给子生成器,传输过程中委派生成器不知道里面的内容是什么,它就像一个敬业的快递员。
代码输出:
>> 0
>> 1
>> 2
>> 3
发送异常到子生成器
#!/usr/bin/python3
def my_printer():
while True:
try:
w = yield
except MyException:
print("Oh bug!")
else:
print('>> ', w)
def my_wrapper(coro):
yield from coro
class MyException(Exception):
pass
w = my_printer()
wrap = my_wrapper(w)
wrap.send(None) #启动生成器,不可少
for i in [0, 1, 2, 'E', 4]:
if i == 'E':
wrap.throw(MyException) #异常会传给my_printer
else:
wrap.send(i)
上面的代码在子生成器中增加了异常处理的逻辑,当调用者抛给(通过"throw"方法)子生成器自定义异常时子生成器会收到并处理,如果子生成器没有处理,异常会冒泡返回给调用者。
代码输出:
>> 0
>> 1
>> 2
Oh bug!
>> 4
总结
"yield from"是全新的语言结构,它的作用比yield多很多。"yield from x"表达式对x对象所作的第一件事是,调用iter(x),从中获取迭代器。因此x可以是任何可迭代的对象。"yield from"的主要功能是产生双向通道,把最外层的调用方与最内层的子生成器连接起来,这样两者可以直接发送和产出值,还可以直接传入异常,而不用在位于中间的委派生成器中添加大量处理异常的样板代码。
希望这篇文章能帮到正在努力的你,欢迎点赞评论!