Python 实现协程(二)

一. 预激活协程的装饰器

调用协程函数后,返回的是一个协程对象,函数本身并不会执行。所以在调用 send 方法前,必须使用 next()send(None) 来预激活协程函数,使协程函数执行到第一个 yield 表达式,处于暂停状态。

为了简化操作,下面我们定义一个预激活的装饰器:

from functools import wraps
 
def coroutine(func):
    """装饰器:向前执行到第一个yield表达式,预激func"""
    @wraps(func)
    def primer(*args, **kwargs):
        gen = func(*args, **kwargs)
        next(gen)
        return gen
    return primer
 
 
@coroutine
def average():
    total = 0
    count = 0
    average = None
    while True:
        term = yield average
        total += term
        count += 1
        average = total / count

我们定义的装饰中,把被装饰的生成器函数替换成这里的 primer 函数,调用 primer 函数时,返回预激活后的生成器。接下来使用 @coroutine 装饰器装饰 average() 协程函数。

运行结果:调用上述模块中定义的 average() 函数创建一个生成器对象,因为在 coroutine 装饰器的 primer 函数中已经预激活了生成器,所以 getgeneratorstate(coro_ava) 返回 GEN_SUSPENDED 状态,因此协程已经准备好,可以接收值了。

很多框架都提供了处理协程的特殊装饰器,不过不是所有装饰器都用于预激协程,有些会提供其它服务,例如勾入事件循环。 比如说,异步网络库 Tornado 提供了 tornado.gen 装饰器。

另外,使用 yield from 句法调用协程时,会自动预激。python 标准库里的 asyncio.coroutine 装饰器不会预激协程,因此能兼容 yield from 句法。

二. 终止协程和异常处理

协程中未处理的异常会向上冒泡,传给 next() 函数或 send() 方法的调用方(即触发协程的对象)。因此,未处理的异常会导致协程终止。以上面的 coro_ava 协程实例为例:

由于发送给 average() 函数的值不是数字,因此协程内有异常抛出。由于在协程内没有处理异常,协程会终止。如果视图重新激活协程,会抛出 StopIteration 异常。

上述示例暗示了终止协程的一种方式,发送一个异常给协程。Python2.5 开始,客户端代码可以在生成器对象上调用 2 个方法,显示地把异常发给协程:

  • generator.throw(exc_type[, exc_value[, traceback]]):使生成器在暂停的 yield 表达式处抛出指定异常。如果生成器处理了抛出的异常,代码会向前执行到下一个 yield 表达式,而产出的值会成为调用 generator.throw 方法得到的返回值。如果生成器没有处理抛出的异常,异常会向上冒泡,传到调用方的上下文中。

  • generator.close():使生成器在暂停的 yield 表达式处抛出 GeneratorExit 异常。如果生成器没有处理这个异常,或者抛出了 StopIterator 异常,调用方不会报错。生成器抛出的其它异常会向上冒泡,传给调用方。

示例代码 在协程中处理异常

class DemoException(Exception):
    """为这次演示定义的异常类型"""
 
 
def demo_exc_handler():
    print('-> coroutine started')
    while True:
        try:
            x = yield
        except DemoException:
            print('*** DemoException handled. Continue ***')
        else:
            print(f'-> coroutine received:{x}')
    raise RuntimeError('This line should never run.')

演示 1 close 终止协程,不会有异常

演示 2 如果把 DemoException 异常传入 demo_exc_handler ,不会导致协程终止

演示 3 如果无法处理传入的异常,协程会终止

如果不管协程如何结束都想做些清理工作,要把协程定义体中相关的代码放入 try / finally 块中。

下面我们改进上述 demo_exc_handler 协程函数的定义,使用 try / finally 在协程终止时执行操作:

class DemoException(Exception):
    """为这次演示定义的异常类型"""
 
 
def demo_finally():
    print('-> coroutine started')
    try:
        while True:
            try:
                x = yield
            except DemoException:
                print('*** DemoException handled. Continue ***')
            else:
                print(f'-> coroutine received:{x}')
    finally:
        print('-> coroutine ending')

演示 使用 try / finally 在协程终止时执行操作

Python 3.3 引入 yield from 结构的主要原因之一与把异常传入嵌套的协程有关。另一个原因是让协程更方便的返回值,下节将介绍让协程返回值以及 yield from 的用法。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,001评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,210评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,874评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,001评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,022评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,005评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,929评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,742评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,193评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,427评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,583评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,305评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,911评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,564评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,731评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,581评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,478评论 2 352

推荐阅读更多精彩内容