python 协程
文章目錄
- 1. 協(xié)程工作流程和狀態(tài)
- 2. 預(yù)激協(xié)程的裝飾器
- 3. 終止協(xié)程、異常處理
- 4. 讓協(xié)程返回值
- 5. yield from
learn from 《流暢的python》
1. 協(xié)程工作流程和狀態(tài)
def simple_coroutine(): # 協(xié)程使用生成器函數(shù)定義,有yield關(guān)鍵字print("-> coroutine started")x = yield # yield 右邊沒有表達(dá)式,所以只需從客戶那里接收數(shù)據(jù)print("-> coroutine received: ", x)my_coro = simple_coroutine() # 調(diào)用函數(shù),得到生成器對(duì)象 print(my_coro) # <generator object simple_coroutine at 0x00000192FD458E40> print(next(my_coro)) # 調(diào)用next 到 yield 處暫停 # -> coroutine started # None my_coro.send(24) # 調(diào)用send,yield 會(huì)計(jì)算出24,之后協(xié)程恢復(fù), # 一直運(yùn)行到下一個(gè) yield 表達(dá)式,或者到達(dá)終止迭代 # -> coroutine received: 24 # Traceback (most recent call last): # File "D:/gitcode/Python_learning/fluent_python/coroutine.py", line 12, in <module> # my_coro.send(24) # StopIteration可以查看協(xié)程的狀態(tài) print(inspect.getgeneratorstate((my_coro))),4種狀態(tài)
- GEN_CREATED 等待開始
- GEN_RUNNING 正在執(zhí)行(多線程中可見)
- GEN_SUSPENDED 在 yield 表達(dá)式處暫停
- GEN_CLOSED 執(zhí)行結(jié)束
啟動(dòng)協(xié)程:
- 調(diào)用 next()
- 調(diào)用 obj.send(None),必須是 None,否則報(bào)錯(cuò)
計(jì)算平均數(shù)的例子:
2. 預(yù)激協(xié)程的裝飾器
使用 next, 或者 send(None)
from functools import wrapsdef coroutine(func):@wraps(func)def primer(*args, **kwargs): # 被裝飾的生成器函數(shù)替換成 primer函數(shù)gen = func(*args, **kwargs) # 調(diào)用被裝飾的函數(shù),獲取生成器對(duì)象next(gen) # 預(yù)激生成器return gen # 返回生成器return primer- 用 yield from 句法調(diào)用協(xié)程時(shí),會(huì)自動(dòng)預(yù)激
- asyncio.coroutine 裝飾器不會(huì)預(yù)激協(xié)程,因此 能兼容 yield from 句法
3. 終止協(xié)程、異常處理
協(xié)程中未處理的異常會(huì)向上冒泡,傳給 next 函數(shù)或 send 方法的調(diào)用方(即觸發(fā)協(xié)程的對(duì)象)。
- generator.throw(exc_type[, exc_value[, traceback]])
- generator.close()
- 處理了異常,協(xié)程可以繼續(xù)執(zhí)行
- 沒有處理異常,發(fā)生異常,協(xié)程終止
- 如果不管協(xié)程如何結(jié)束都想做些清理工作,要把協(xié)程定義體中相關(guān)的代碼放入 try/finally 塊中
4. 讓協(xié)程返回值
from collections import namedtuple res = namedtuple("Result", "count average")def averager():tot = 0.0count = 0avg = Nonewhile True:term = yieldif term is None:break # 為了返回值,協(xié)程必須正常終止tot += termcount += 1avg = tot/countreturn res(count, avg) coro_avg = averager() next(coro_avg) coro_avg.send(10) coro_avg.send(20) coro_avg.send(30) coro_avg.send(None) # 發(fā)送None終止循環(huán),協(xié)程結(jié)束 # Traceback (most recent call last): # File "D:/gitcode/Python_learning/fluent_python/coroutine.py", line 170, in <module> # coro_avg.send(None) # StopIteration: Result(count=3, average=20.0)- 如何獲取協(xié)程的返回值,捕獲異常,讀取異常的 value
5. yield from
在其他語言 中,類似的結(jié)構(gòu)使用 await 關(guān)鍵字
- 在生成器 gen 中使用 yield from subgen() 時(shí),subgen 會(huì)獲得控制權(quán),把產(chǎn)出的值傳給 gen 的調(diào)用方,即調(diào)用方 可以直接控制 subgen
- 與此同時(shí),gen 會(huì)阻塞,等待 subgen 終止
yield from x :
- 首先調(diào)用 iter(x),獲取迭代器
yield from 的主要功能是打開雙向通道,把最外層的調(diào)用方與最內(nèi)層 的子生成器連接起來,這樣二者可以直接發(fā)送和產(chǎn)出值,還可以直接傳入異常,而不用在位于中間的協(xié)程中添加大量處理異常的樣板代碼。
有了這個(gè)結(jié)構(gòu),協(xié)程可以通過 以前不可能的方式委托職責(zé)
上面圖中左側(cè) 外層 for 循環(huán)的末尾沒有 group.send(None),則子生成器不終止,委派生成器 會(huì)在 yield from 處永遠(yuǎn)暫停
- 還可以用協(xié)程做 離散事件仿真
- 如果想使用現(xiàn)成的 Python 協(xié)程庫,可以使用 SimPy
總結(jié)
- 上一篇: 天池 在线编程 区间统计(队列)
- 下一篇: LeetCode 2040. 两个有序数