# <generator object simple_coroutine at 0x000002601BE2E200> # Coroutines started
如果创建协程对象后立即把None之外的值发给它,会出现下述错误:can’t send non-None value to a just-started generator.
1 2 3 4 5 6 7 8 9
cor = simple_coroutine() cor.send(123)
Traceback (most recent call last): File "D:\Python3.6.0\lib\site-packages\IPython\core\interactiveshell.py", line 2961, in run_code exec(code_obj, self.user_global_ns, self.user_ns) File "<ipython-input-12-3cd430c98ae0>", line 1, in <module> cor.send(123) TypeError: can't send non-None value to a just-started generator
defcoroutine(func): # 装饰器:向前执行到第一个yield表达式,预激func @wraps(func) defprimer(*args, **kwargs): gen = func(*args, **kwargs) next(gen) return gen return primer
@coroutine defaverage_primer(): total, count = 0, 0 average = None whileTrue: var = yield average total += var count += 1 average = total / count
if __name__ == '__main__': from inspect import getgeneratorstate avg_primer = average_primer() print(getgeneratorstate(avg_primer)) # GEN_SUSPENDED print(avg_primer.send(1)) print(avg_primer.send(3)) print(avg_primer.send(5))
发送 None 会终止循环,导致协程结束,返回结果。一如既往,生成器对象会抛出StopIteration 异常。异常对象的 value 属性保存着返回的值。
何获取协程返回的值: 把 avg.send(None) 用try包含。
1 2 3 4 5
try: avg.send(None) except StopIteration as e: print(e) # Result(count=3, average=2.0)
使用yield from
yield from 结构会在内部自动捕获 StopIteration 异常。这种处理方式与for 循环处理StopIteration 异常的方式一样:循环机制使用用户易于理解的方式处理异常。对 yield from 结构来说,解释器不仅会捕获 StopIteration 异常,还会把 value 属性的值变成 yield from 表达式的值。
yield from 可用于简化 for 循环中的 yield 表达式:
1 2 3 4 5 6 7
defgen(): for i in"ABC": yield i for j in [1, 2, 3]: yield j
print(list(gen())) # ['A', 'B', 'C', 1, 2, 3]
使用yield from:
1 2 3 4 5
defgen2(): yieldfrom"ABC" yieldfrom [1, 2, 3]
print(list(gen2())) # ['A', 'B', 'C', 1, 2, 3]
yield from 在扁平化处理嵌套型的序列中的应用:
1 2 3 4 5 6 7
defgen3(*args): for item in args: yieldfrom item
a = (1, 2, 3) b = "ABC" print(list(gen3(a, b))) # [1, 2, 3, 'A', 'B', 'C']
yield from x 表达式对 x 对象所做的第一件事是,调用 iter(x),从中获取迭代器,因此 x 可以是任何可迭代的对象。
如果 yield from 结构唯一的作用是替代产出值的嵌套 for 循环,这个结构很有可能不会添加到 Python 语言中。yield from 结构的本质作用无法通过简单的可迭代对象说明,而要发散思维,使用嵌套的生成器。
yield from 的主要功能是打开双向通道,把最外层的调用方与最内层的子生成器连接起来,这样二者可以直接发送和产出值,还可以直接传入异常,而不用在位于中间的协程中添加大量处理异常的样板代码。有了这个结构,协程可以通过以前不可能的方式委托职责。