協(xié)程

從句法上來看,協(xié)程與生成器類似,都是定義體中包含yield的函數(shù)??墒?,在協(xié)程中,yield通常出現(xiàn)在表達式的右邊(例如,datum=yield),可以產(chǎn)出值,也可以不產(chǎn)出--如果yield關(guān)鍵字后面沒有表達式,那么生成器產(chǎn)出None。協(xié)程可能會從調(diào)用方接收數(shù)據(jù),不過調(diào)用方把數(shù)據(jù)提供給協(xié)程使用的是.send(datum)方法,而不是next(...)函數(shù)。通常,調(diào)用方會把值推送給協(xié)程。

從根本上把yield視作控制流程的方式,就好理解協(xié)程了。



生成器如何進化成協(xié)程

生成器的調(diào)用方可以使用.send(...)方式發(fā)送數(shù)據(jù),發(fā)送的數(shù)據(jù)會成為生成器函數(shù)中yield表達式的值。

用作協(xié)程的生成器的基本行為


協(xié)程可以身處以下四個狀態(tài)中的一個:

‘GEN_CREATE’:等待開始執(zhí)行

‘GEN_RUNNING’:解釋器正在執(zhí)行

‘GEN_SUSPENDED’:在yield表達式處暫停

‘GEN_CLOSED’:執(zhí)行結(jié)束

因為send方法的參數(shù)會成為暫停的yield的表達式的值,所以,僅當協(xié)程處于暫停狀態(tài)時才能調(diào)用send方法

最先調(diào)用next(my_coro)函數(shù)這一步通常稱為“預(yù)激”(即讓協(xié)程向前執(zhí)行到第一個yield表達式,準備好作為活躍的協(xié)程使用)

下面舉個產(chǎn)出多個值的例子:


1、調(diào)用next(my_coro2),打印第一個消息,然后執(zhí)行yield a,產(chǎn)出數(shù)字14

2、調(diào)用my_coro2.send(28),把28賦給b,打印第二個消息,然后執(zhí)行yield a+b,產(chǎn)出數(shù)字42

3、調(diào)用my_coro2.send(99),把99賦值給c,打印第三個消息,協(xié)程終止。

使用協(xié)程計算移動平均值


這里的無限循環(huán)表明,只要調(diào)用方不斷把值發(fā)給這個協(xié)程,它就會一直接受值,然后生成結(jié)果,僅當調(diào)用方在協(xié)程上調(diào)用.close()方法,或者沒有對協(xié)程的引用而被垃圾回收程序回收時,這個協(xié)程才會終止。


預(yù)激協(xié)程的裝飾器

如果不預(yù)激,那么協(xié)程沒有什么用,在上例中,調(diào)用my_coro.send(x)之前,記住一定要調(diào)用next(my_coro)。為了簡化協(xié)程的用法,有時會使用一個預(yù)激裝飾器。


1、把被裝飾的生成器函數(shù)替換成這里的primer函數(shù);調(diào)用primer函數(shù)時,返回預(yù)激的生成器。

2、調(diào)用被裝飾的函數(shù),獲取生成器對象。

3、預(yù)激生成器。

4、返回生成器。

終止協(xié)程和異常處理


上例暗示了終止協(xié)程的一種方式,發(fā)送某個哨符值,讓協(xié)程退出。內(nèi)置的None和Ellipsis等常量經(jīng)常用做哨符值??蛻舸a可以在生成器對象上調(diào)用兩個方法,顯式地把異常發(fā)給協(xié)程。這兩個方法是throw和close。

generator.throw

致使生成器在暫停的yield表達式處拋出指定的異常。如果生成器處理了拋出的異常,代碼會向前執(zhí)行到下一個yield表達式,而產(chǎn)出的值會成為調(diào)用generator.throw方法得到的返回值。

generator.close()

致使生成器在暫停的yield表達式處拋出GeneratorExit異常。

下面舉例說明如何使用close和throw方法控制協(xié)程。


如果把DemoException異常傳入demo_exc_handling協(xié)程,它會處理,然后繼續(xù)運行,如下例所示:



如果無法處理傳入的異常,則協(xié)程會停止


如果不管協(xié)程如何結(jié)束都想做些清理工作,要把協(xié)程定義體中相關(guān)的代碼放入try/finally塊中,如下例所示:


讓協(xié)程返回值


1、為了返回值,協(xié)程必須正常終止,因此,這一版averager中又個條件判斷,以便退出累計循環(huán)。

2、返回一個named tuple,包含count和average兩個字段

下例說明了如何使用協(xié)程返回值

然而在上例中,return表達式的值會偷偷傳給調(diào)用方,賦值給StopIteration異常的一個屬性。這樣做有點不合理,下例展示如何通過捕捉異常捕獲協(xié)程返回的值。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容