一、yield
关于yield详细可参考我这篇文章
下面是一个带yield的生成器:
def gen_yield():
while True:
recv = yield
do something with recv
现在我们不单独使用gen_yield这生成器,而是通过另一个携程outer_yield完成必要的验证、设置等再去使用它
def outer_yield():
chiled_gen = gen_yield()
chiled_gen.send(None) #准备上子携程
do something just like setting or you want #在这儿做一些设置或者验证工作
while True:
recv = yield
chiled_gen.send(recv) #把上面接收到的消息,发送到子携程处理
以上可知,yield 只允许协程与外界直接交互而不能使得子协程与外界直接交互。而且,我们这里还没有考虑子协程抛出异常,返回等情况。
yield form 应运而生来解决这个问题:
def gen_yield():
while True:
recv = yield
do something with recv
def new_outer():
do something you want
yield from gen_yield() #1 yield from 后必须跟generator object
no = new_outer()
no.send(None) #2启动生成器
这儿我们可以理解为#1 处yield from 把工作下发(委托)给了gen_yield()子生成器
而当#2出外界开始send值与outer交互时,outer直接通过yield from把值发送给子生成器gen_yield执行
这样就达到了外界直接与子生成器直接交互的功能。
总结:
- 所有new_outer的实例作为调用者把值send过去,都等于直接发送给了子迭代器gen_yield,而子迭代器yield回来的值也登月直接给了调用者。意思就是nwe_outer委托gen_yield工作。
- 当new_outer实例调用send方法,就等于gen_yield子迭代器同步调用send方法,把值send到子迭代器内部yield。
- 当子迭代器gen_yield内部捕获到StopIteration时,子迭代器内部停止执行,开始执行委托者new_outer.
- 委托生成器有一个 throw() 方法可发送任何除了 GeneratorExit 的异常都将被直接发送给子迭代器 。同理,如果这次调用产生了 StopIteration 异常,那么委托生成器 new_outer 将被恢复执行
- 如果向委托生成器发送了 GeneratorExit 异常或者调用委托生成器的 close() 方法,那么首先子迭代器 b 的 close() 方法就会被调用(如果有的话)。随后,委托生成器就会引发 GeneratorExit 异常,从而退出
- 如果迭代器 gen_yield中有 return value ,那么当 子迭代器 执行 return 返回时,等价于 raise StopIteration(value)(迭代器总是通过引发StopIteration 异常停止),之后该 value 将成为 yield from 表达式值。
- 在任意运行时刻,如果迭代器gen_yield抛出了未捕获的异常,那么该异常将会被传播给委托生成器new_outer
带return值的列子
>>> def inner_gen():
... sum_all = 0
... while True:
... x = yield
... print('recv : {0}'.format(x))
... if x is None:
... return sum_all
... sum_all = sum_all + x
...
>>> def outer():
... while True:
... sum_all = yield from inner_gen()
... print('sum_all is: {0}'.format(sum_all))
... print('outer 执行完毕')
...
>>> out = outer()
>>> out.send(None) # 准备好协程
>>> for i in range(3):
... out.send(i)
...
recv: 0
recv: 1
recv: 2
>>> out.send(None) # 停止子协程
recv : None
sum_all is: 3
outer 执行完毕
>>> for i in range(5):
... out.send(i)
...
recv: 0
recv: 1
recv: 2
recv: 3
recv: 4
>>> out.send(None)
recv: None
sum_all is: 10
outer 执行完毕
>>> out.throw(TypeError) # TypeError 来自 inner_gen 函数
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in outer
File "<stdin>", line 4, in inner_gen
TypeError
使用 send() 方法发送的值将被直接发送字生成器 inner_gen() 中了,throw() 同理。当子迭代器返回时,return 后的值成为 yield from 表达式的值。
yield from 允许外界与子协程直接交互,这样就允许代码重构:把一部分包含 yield 的代码放到另外的函数,再使用 yield from 调用该迭代器。这样就实现委托工作了