從句法上來看,協(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é)程返回的值。