從句法上看,協程與生成器類似,都是定義體中包含 yield 關鍵字的函數。可是,在協程中, yield 通常出現在表達式的右邊(例如, datum = yield),可以產出值,也可以不產出 —— 如果 yield 關鍵字后面沒有表達式,那么生成器產出 None。
協程可能會從調用方接收數據,不過調用方把數據提供給協程使用的是 .send(datum) 方法,而不是next(…) 函數。
==yield 關鍵字甚至還可以不接收或傳出數據。不管數據如何流動, yield 都是一種流程控制工具,使用它可以實現協作式多任務:協程可以把控制器讓步給中心調度程序,從而激活其他的協程==。
協程的生成器的基本行為
這里有一個最簡單的協程代碼:
def simple_coroutine(): print('-> start') x = yield print('-> recived', x)sc = simple_coroutine()next(sc)sc.send('zhexiao')
解釋:
1. 協程使用生成器函數定義:定義體中有 yield 關鍵字。
2. yield 在表達式中使用;如果協程只需從客戶那里接收數據,那么產出的值是 None —— 這個值是隱式指定的,因為 yield 關鍵字右邊沒有表達式。
3. 首先要調用 next(…) 函數,因為生成器還沒啟動,沒在 yield 語句處暫停,所以一開始無法發送數據。
4. 調用send方法,把值傳給 yield 的變量,然后協程恢復,繼續執行下面的代碼,直到運行到下一個 yield 表達式,或者終止。
==注意:send方法只有當協程處于 GEN_SUSPENDED 狀態下時才會運作,所以我們使用 next() 方法激活協程到 yield 表達式處停止,或者我們也可以使用 sc.send(None),效果與 next(sc) 一樣==。
協程的四個狀態:
協程可以身處四個狀態中的一個。當前狀態可以使用inspect.getgeneratorstate(…) 函數確定,該函數會返回下述字符串中的一個:
1. GEN_CREATED:等待開始執行
2. GEN_RUNNING:解釋器正在執行
3. GEN_SUSPENED:在yield表達式處暫停
4. GEN_CLOSED:執行結束
==最先調用 next(sc) 函數這一步通常稱為“預激”(prime)協程==(即,讓協程向前執行到第一個 yield 表達式,準備好作為活躍的協程使用)。
import inspectdef 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, 6sc.send(7) # 5, 6, 7
示例:使用協程計算移動平均值
def averager(): total = 0.0 count = 0 avg = None while True: num = yield avg total += num count += 1 avg = total/count# runag = averager()# 預激協程print(next(ag)) # Noneprint(ag.send(10)) # 10print(ag.send(20)) # 15
新聞熱點
疑難解答