python中终止协程和异常处理方式
目录
未处理的异常会导致协程终止下面举例说明总结协程中未处理的异常会向上冒泡,传给 next 函数或 send 方法的调用方(即触发协程的对 象)。
下面示例举例说明如何使用之前博客示例中由装饰器定义的 averager 协程。
未处理的异常会导致协程终止
""" 预激协程的装饰器 """ from inspect import getgeneratorstate from functools import wraps def coroutine(func): """装饰器:向前执行到第一个`yield`表达式,预激`func`""" # 把被装饰的生成器函数替换成这里的 primer 函数; # 调用 primer 函数时,返回预激后的 生成器。 @wraps(func) def primer(*args, **kwargs): # 调用被装饰的函数,获取生成器对象。 gen = func(*args, **kwargs) # 预激生成器。 next(gen) # 返回生成器。 return gen return primer @coroutine def averager(): total = 0.0 count = 0 average = None while True: term = yield average total += term count += 1 average = total / count if __name__ == "__main__": coro_avg = averager() # print(getgeneratorstate(coro_avg)) print(coro_avg.send(10)) print(coro_avg.send(30)) # 发送的值不是数字,导致协程内部有异常抛出。 print(coro_avg.send("spam")) # 由于在协程内没有处理异常,协程会终止。 # 如果试图重新激活协程,会抛出 StopIteration 异常。 print(coro_avg.send(60))
上面示例,暗示了终止协程的一种方式:发送某个哨符值,让协程退出。内置的 None 和 Ellipsis 等常量经常用作哨符值。Ellipsis 的优点是,数据流中不太常有这个值。我还见 过有人把 StopIteration 类(类本身,而不是实例,也不抛出)作为哨符值;也就是说, 是像这样使用的:my_coro.send(StopIteration)。
【资料图】
从 Python 2.5 开始,客户代码可以在生成器对象上调用两个方法,显式地把异常发给协程。
这两个方法是 throw 和 close。
generator.throw(exc_type[, exc_value[, traceback]])
致使生成器在暂停的 yield 表达式处抛出指定的异常。
如果生成器处理了抛出的异常,代码会向前执行到下一个 yield 表达式,而产出的值会成为调用 generator.throw 方法 得到的返回值。
如果生成器没有处理抛出的异常,异常会向上冒泡,传到调用方的上下 文中。
generator.close()
致使生成器在暂停的yield 表达式处抛出GeneratorExit 异常。
如果生成器没有处 理这个异常,或者抛出了StopIteration 异常(通常是指运行到结尾),调用方不会 报错。
如果收到GeneratorExit 异常,生成器一定不能产出值,否则解释器会抛出 RuntimeError 异常。
生成器抛出的其他异常会向上冒泡,传给调用方。
下面举例说明
如何使用 close 和 throw 方法控制协程:
""" 学习在协程中处理异常的测试代码 """ from inspect import getgeneratorstate class DemoException(Exception): """为这次演示定义的异常类型。""" def demo_exc_handling(): print("-> coroutine started") try: while True: try: x = yield # 特别处理 DemoException 异常 except DemoException: print("*** DemoException handled. Continuing...") # 如果没有异常,那么显示接收到的值。 else: print("-> coroutine received: {!r}".format(x)) finally: # 如果不管协程如何结束都想做些清理工作, # 要把协程定义体中相关的代码放入try/ finally 块中 print("-> coroutine ending") if __name__ == "__main__": exc_coro = demo_exc_handling() next(exc_coro) exc_coro.send(11) exc_coro.send(22) # 激活和关闭 demo_exc_handling,没有异常 # exc_coro.close() # 如果把 DemoException 异常传入 demo_exc_handling 协程, # 它会处理,然后继续运行 # exc_coro.throw(DemoException) # exc_coro.send(33) # 如果无法处理传入的异常,协程会终止 exc_coro.throw(ZeroDivisionError) print(getgeneratorstate(exc_coro))
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
X 关闭
X 关闭
- 15G资费不大降!三大运营商谁提供的5G网速最快?中国信通院给出答案
- 2联想拯救者Y70发布最新预告:售价2970元起 迄今最便宜的骁龙8+旗舰
- 3亚马逊开始大规模推广掌纹支付技术 顾客可使用“挥手付”结账
- 4现代和起亚上半年出口20万辆新能源汽车同比增长30.6%
- 5如何让居民5分钟使用到各种设施?沙特“线性城市”来了
- 6AMD实现连续8个季度的增长 季度营收首次突破60亿美元利润更是翻倍
- 7转转集团发布2022年二季度手机行情报告:二手市场“飘香”
- 8充电宝100Wh等于多少毫安?铁路旅客禁止、限制携带和托运物品目录
- 9好消息!京东与腾讯续签三年战略合作协议 加强技术创新与供应链服务
- 10名创优品拟通过香港IPO全球发售4100万股 全球发售所得款项有什么用处?