学Python,不明白协程那就亏大了

学习Python很多书籍都或多或少介绍协程,但是很少有人会在开发中使用,一是理解不深,使用怕出错,二是有其他方式可以替代,知识代码多一点,便于理解啊,总之:不使用的理由千千种。
今天我就来和大家聊聊Python协程

  1. 协程是啥

  2. 协程在实际的应用

协程的定义

协程 是为非抢占式多任务产生子程序的计算机程序组件,协程允许不同入口点在不同位置暂停或开始执行程序”。从技术的角度来说,“协程就是你可以暂停执行的函数”

与生成器类似,但yield 出现在表达式的右边,且产出一个表达式值作为返回值,如果yield后没有表达式,则返回None。协程可以从调用方接收数据,caller.send(params)。

1. 例子
>>> def simple(a):
    print "start:a = ", (a)
    b = yield a
    print "received b= ",(b)
    c = yield a + b
    print "received :d ", (c)

    
>>> m1 = simple(2)
>>> m1
<generator object simple at 0x0000000003D830D8>
>>> next(m1) # 激活协程, 并产出a,然后暂停
start:a =  2
2
>>> m1.send(3)
received b=  3
5
>>> m1.send(1000) # b 接收数据,并产出 a+b 的结果,然后暂停 
received :d  1000

Traceback (most recent call last):
  File "<pyshell#11>", line 1, in <module>
    m1.send(1000)
StopIteration

yield作为控制流程的方式来理解。

image.png

上图中 第一阶段 yield a结束,第二阶段是给b 赋值开始

再看一个例子:

def averager():
    total = 0
    count = 0
    avg = None
    while True:
        term = yield avg 
        total += term
        count +=1
        avg = total/count

ag = averager()
next(ag) # 运行到 yield avg 就停止,并返回avg值 None
ag.send(100) # item 接收到100,并运行,直到下一个yield 并停止

2. 与协程相关的方法

我们知道了怎么创建协程,但是当执行到最后,如果不处理就会和生成器一样抛出一个stopInteration异常来终止该协程,从2.5版本开始添加了两个方法throwclose来终止协程

如果generator.thow(exception)的异常被捕获,则继续下一个yield,否则终止

3. 协程返回值

在定义体中 return value

from collections import namedTuple
Result = namedTuple("Result","count average")

def average():
      total = 0
      count = 0
      avg = None
      while True:
             term = yield
             if term is None:
                break   # 终止符
             total +=term
             count +=1
             avg = total/count
     return Result(count,avg)

send(None) 就会break, 返回。其中Result 会作为异常的value属性返回

try:
     m2 .send(None)
exception StopInteration as exc:
     print exc.value

应用



# BEGIN YIELD_FROM_AVERAGER
from collections import namedtuple

Result = namedtuple('Result', 'count average')


# the subgenerator
def averager():  # <1>
    total = 0.0
    count = 0
    average = None
    while True:
        term = yield  # <2>
        if term is None:  # <3>
            break
        total += term
        count += 1
        average = total/count
    return Result(count, average)  # <4>


# the delegating generator
def grouper(results, key):  # <5>
    while True:  # <6>
        results[key] = yield from averager()  #生成一个使用协程的生成器,在此处暂停,等 将一个Result 赋值给对应的key


# the client code, a.k.a. the caller
def main(data):  # <8>
    results = {}
    for key, values in data.items():
        group = grouper(results, key)  # group是grouper函数生成的生成器对象
        next(group)  # 预激活协程
        for value in values:
# 把各个 value 传给 grouper。传入的值最终到达 averager 函数中 term = yield 那一行; grouper 永远不知道传入的值是什么
            group.send(value)  

# 内层循环结束后, group 实例依旧在 yield from 表达式处暂停,因此, grouper函数定义体中为 results[key] 赋值的语句还没有执行

#把 None 传入 grouper,导致当前的 averager 实例终止,也让 grouper 继续运行,再创建一个 averager 实例,处理下一组值
        group.send(None) 

    # print(results)  # uncomment to debug
    report(results)


# output report
def report(results):
    for key, result in sorted(results.items()):
        group, unit = key.split(';')
        print('{:2} {:5} averaging {:.2f}{}'.format(
              result.count, group, result.average, unit))


data = {
    'girls;kg':
        [40.9, 38.5, 44.3, 42.2, 45.2, 41.7, 44.5, 38.0, 40.6, 44.5],
    'girls;m':
        [1.6, 1.51, 1.4, 1.3, 1.41, 1.39, 1.33, 1.46, 1.45, 1.43],
    'boys;kg':
        [39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3],
    'boys;m':
        [1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46],
}


if __name__ == '__main__':
    main(data)

上述例子,如果子生成器不停止,委派生成器永远在yield from 处停止。

yield from iterable本质上等于for item in iterable: yield item的缩写

yield from的意义

把迭代器当作生成器使用,相当于把子生成器的定义体内联在 yield from 表达式
中。此外,子生成器可以执行 return 语句,返回一个值,而返回的值会成为 yield
from 表达式的值

  1. 子生成器产出的值都直接传给委派生成器的调用方(即客户端代码将上述例子的averager 直接给了main中的group)。

  2. 使用 send() 方法发给委派生成器的值都直接传给子生成器。如果发送的值是
    None,那么会调用子生成器的 __next__()方法。如果发送的值不是None,那么会
    调用子生成器的 send()方法。如果调用的方法抛出 StopIteration 异常,那么委
    派生成器恢复运行。任何其他异常都会向上冒泡,传给委派生成器。

  3. 生成器退出时,生成器(或子生成器)中的return expr表达式会触发
    StopIteration(expr)异常抛出。

  4. yield from 表达式的值是子生成器终止时传给 StopIteration 异常的第一个参
    数。

** yield from 结构的另外两个特性与异常和终止有关。**

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

推荐阅读更多精彩内容