Python-学习之路-16 协程

协程

迭代器

  • 可迭代(iterable):可直接作用for循环的变量
  • 迭代器(iterator):不经可以作用域for循环,还可以倍next调用
  • list是一个典型的可迭代对象,但不是迭代器
  • 通过 isinstance判断
  • iterable和iterator是可以互相转换的
    • 通过iter函数
#isinstance案例

from collections import Iterable
from collections import Iterator

ll = [1,2,3,4,5,6]
#判断是否可迭代
print(isinstance(ll,Iterable))
#判断是否是迭代器
print(isinstance(ll,Iterator))

#转换为迭代器
ll_iter = iter(ll)
#判断是否可迭代
print(isinstance(ll_iter,Iterable))
#判断是否是迭代器
print(isinstance(ll_iter,Iterator))
True
False
True
True

生成器

  • generator:一边循环一边计算下一个元素的机制\算法
  • 需满足三个条件:
    • 每次调用都能够生产出for循环需要的下一个元素
    • 如果达到最后一个后,爆出stopIteration异常
    • 可以被next调用

如果生成一个生成器

  • 直接使用
  • 包含yield,则这个函数就叫生成器
# 直接使用生成器
# 放在中括号中是列表生成器
l = [x*x for x in range(5)]
# 放在小括号中就是生成器
g = (x*x for x in range(5))

print(type(l))
print(type(g))
<class 'list'>
<class 'generator'>
# 函数案例
def odd():
    print("step 1")
    yield 1
    print("step 2")
    yield 2
    
g = odd()
one  = next(g)
print(one)
two  = next(g)
print(two)
step 1
1
step 2
2
# for 循环调用生成器
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a ,b = b, a+b
        n +=1
    # 注意,爆出的异常是return返回的值 
    return 'Done'

g = fib(5)

for i in range(6):
    rst = next(g)
    print(rst)
1
1
2
3
5



---------------------------------------------------------------------------

StopIteration                             Traceback (most recent call last)

<ipython-input-3-e8ff877cca38> in <module>
     12 
     13 for i in range(6):
---> 14     rst = next(g)
     15     print(rst)


StopIteration: Done
ge = fib(10)
'''
生成器的典型用法就是在for循环中使用
'''
for i in ge:
    print(i)
1
1
2
3
5
8
13
21
34
55

参考资料来自:https://blog.csdn.net/andybegin/article/details/77884645

协程的历史

  • 3.4 引入协成,用yield实现
  • 3.5 引入协成语法
  • 实现协程比较好的包有 asyncio,tornado,gevent
  • 从技术角度讲,协成就是一个可以暂停执行的函数,或者可以把协成理解成生成器
  • 协成的实现
    • yield 返回
    • send 调用

协程的四个状态

  • 协程可以身处四个状态中的一个。当前状态可以使用inspect.getgeneratorstate(…) 函数确定,该函数会返回下述字符串中的一个:
    1. GEN_CREATED:等待开始执行
    2. GEN_RUNNING:解释器正在执行
    3. GEN_SUSPENED:在yield表达式处暂停
    4. GEN_CLOSED:执行结束
#协成的案例1
def simple_coroutine():
    print("-->start")
    x = yield
    print("-->recived",x)
    
# 主线程
sc = simple_coroutine()
print(1111)

# 预激(可以理解为激活协程)
# 同时可以使用se.send(None) 效果与next(sc)一致
next(sc)

print(22222)
#调用协程
sc.send("wangdana")
1111
-->start
22222
-->recived wangdana



---------------------------------------------------------------------------

StopIteration                             Traceback (most recent call last)

<ipython-input-3-9b0f93417b35> in <module>
     15 print(22222)
     16 #调用协程
---> 17 sc.send("wangdana")


StopIteration: 
# 协成案例2
def simple_coroutine(a):
    print('-> start') 
    b = yield a 
    print('-> recived', a, b) 
    c = yield a + b 
    print('-> recived', a, b, c)
# run 
sc = simple_coroutine(5)
next(sc) 
sc.send(6) # 5, 6 
sc.send(7) # 5, 6, 7

-> start
-> recived 5 6
-> recived 5 6 7



---------------------------------------------------------------------------

StopIteration                             Traceback (most recent call last)

<ipython-input-3-e9591ee2bc46> in <module>
     10 next(sc)
     11 sc.send(6) # 5, 6
---> 12 sc.send(7) # 5, 6, 7


StopIteration: 

解释:

  1. 调用 next(ag) 函数后,协程会向前执行到 yield 表达式,产出 average 变量的初始值——None。
  2. 此时,协程在 yield 表达式处暂停。
  3. 使用 send() 激活协程,把发送的值赋给 num,并计算出 avg 的值。
  4. 使用 print 打印出 yield 返回的数据。

终止协程和异常处理

  • 协程中未处理的异常会向上冒泡,传给 next 函数或 send 方法的调用方(即触发协程的对象)。
  • 终止协程的一种方式:发送某个哨符值,让协程退出。内置的 None 和Ellipsis 等常量经常用作哨符值

显式地把异常发给协程

  • 从 Python 2.5 开始,客户代码可以在生成器对象上调用两个方法,显式地把异常发给协程。

generator.throw(exc_type[, exc_value[, traceback]])

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

generator.close()

  • 致使生成器在暂停的 yield 表达式处抛出 GeneratorExit 异常。如果生成器没有处理这个异常,或者抛出了 StopIteration 异常(通常是指运行到结尾),调用方不会报错。如果收到 GeneratorExit 异常,生成器一定不能产出值,否则解释器会抛出RuntimeError 异常。生成器抛出的其他异常会向上冒泡,传给调用方。

yield from获取协程的返回值

  • 为了得到返回值,协程必须正常终止;然后生成器对象会抛出StopIteration 异常,异常对象的 value 属性保存着返回的值。
  • yield from 结构会在内部自动捕获 StopIteration 异常==。对 yield from 结构来说,解释器不仅会捕获 StopIteration 异常,还会把value 属性的值变成 yield from 表达式的值。

yield from基本用法

  • 在生成器 gen 中使用 yield from subgen() 时, subgen 会获得控制权,把产出的值传给 gen 的调用方,即调用方可以直接控制 subgen。与此同时, gen 会阻塞,等待 subgen 终止
# yield from 案例
def gen():
    for c in 'AB':
        yield c

print(list(gen()))

def gen_new():
    yield from 'AB'

print(list(gen_new()))
['A', 'B']
['A', 'B']

yield from高级用法

  • yield from 的主要功能是打开双向通道,把最外层的调用方与最内层的子生成器连接起来,这样二者可以直接发送和产出值,还可以直接传入异常,而不用在位于中间的协程中添加大量处理异常的样板代码

yield from 专门的术语

委派生成器:包含 yield from 表达式的生成器函数。
子生成器:从 yield from 中 部分获取的生成器。
from collections import namedtuple 
ResClass = namedtuple('Res', 'count average') 
# 子生成器 
def averager(): 
    total = 0.0 
    count = 0 
    average = None 
    while True: 
        term = yield 
        if term is None: 
            break 
        total += term 
        count += 1 
        average = total / count 
    return ResClass(count, average) 
# 委派生成器 
def grouper(storages, key): 
    while True: 
        # 获取averager()返回的值 
        storages[key] = yield from averager() 
        
# 客户端代码 
def client(): 
    process_data = { 
        'boys_2': [39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3], 
        'boys_1': [1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46] } 
    storages = {} 
    for k, v in process_data.items(): 
        # 获得协程 
        coroutine = grouper(storages, k) 
        # 预激协程 
        next(coroutine) 
        # 发送数据到协程 
        for dt in v: 
            coroutine.send(dt) 
            # 终止协程 
            coroutine.send(None) 
        print(storages)
# run 
client()

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

推荐阅读更多精彩内容