yield from 可用于简化 for 循环中的 yield 表达式。
yield from x 表达式对 x 对象操作调用 iter(x),从中获取迭代器。因此,x 可以是任何可迭代的对象。
def chain(*its):
for it in its:
yield from it
list(chain(['a', 'b', 'c', 'd'], range(5), ['hello', 'world']))
----------
----------
----------
---------
yield from 的主要功能是打开双向通道,把最外层的调用方与最内层的子生成器连接起来, 这样二者可以直接发送和产出值, 还可以直接传入异常, 而不用在位于中间的协程中添加大量处理异常的样板代码。 有了这个结构, 协程可以通过以前不可能的方式委托职责
def get_sum():
l = []
while True:
num = yield
if num is None:
break
l.append(num)
return {'sum': sum(l), 'list':l}
def delegate(results):
while True:
result = yield from get_sum()
results.append(result)
>>> results = []
>>> summary = delegate(results)
>>> next(summary)
>>> for i in range(10):
... summary.send(i)
...
>>> summary.send(None)
>>> for i in [3,6,1,3,4,5]:
... summary.send(i)
...
>>> summary.send(None)
>>> results
[{'sum': 45, 'list': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}, {'sum': 22, 'list': [3, 6, 1, 3, 4, 5]}]
yield from 的行为
- 子生成器产出的值都直接传给委派生成器的调用方(即客户端代码)。
- 使用 send() 方法发给委派生成器的值都直接传给子生成器。 如果发送的值是 None, 那么会调用子生成器的 __next__() 方法。 如果发送的值不是 None, 那么会调用子生成器的 send() 方法。 如果调用的方法抛出 StopIteration 异常, 那么委派生成器恢复运行。 任何其他异常都会向上冒泡, 传给委派生成器。
- 生成器退出时,生成器(或子生成器) 中的 return expr 表达式会触发 StopIteration(expr) 异常抛出。
- yield from 表达式的值是子生成器终止时传给 StopIteration异常的第一个参数
yield from 另外两个与异常和终止有关特性
- 传入委派生成器的异常, 除了 GeneratorExit 之外都传给子生成器的 throw() 方法。 如果调用 throw() 方法时抛出StopIteration 异常, 委派生成器恢复运行。 StopIteration 之外的异常会向上冒泡, 传给委派生成器。
- 如果把 GeneratorExit 异常传入委派生成器, 或者在委派生成器上调用 close() 方法, 那么在子生成器上调用 close() 方法, 如果它有的话。 如果调用 close() 方法导致异常抛出, 那么异常会向上冒泡, 传给委派生成器; 否则委派生成器抛出GeneratorExit 异常。
#StopIteration 之外的异常会向上冒泡, 传给委派生成器。
def get_sum():
num = yield
raise Exception
return
def delegate(results):
while True:
result = yield from get_sum()
results.append(result)
print('end')
>>> results = []
>>> summary = delegate(results)
>>> next(summary)
>>> summary.send(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in delegate
File "<stdin>", line 3, in get_sum
Exception
#子生成器抛StopIteration 异常,委派生成器恢复运行
def get_sum():
num = yield
raise StopIteration
return num
def delegate(results):
while True:
result = yield from get_sum()
results.append(result)
>>> results = []
>>> summary = delegate(results)
>>> next(summary)
>>> for i in range(10):
... summary.send(i)
...
>>>
>>> summary.send(None)
>>> for i in [3,6,1,3,4,5]:
... summary.send(i)
...
>>> summary.send(None)
>>> results
[None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None]