協程能夠身處四個狀態中的一個。併發
當前狀態能夠使用inspect.getgeneratorstate(...) 函數肯定,該函數會返回下述字符串中的一個。app
'GEN_CREATED'
等待開始執行。函數
'GEN_RUNNING'
解釋器正在執行。spa
'GEN_SUSPENDED'
在 yield 表達式處暫停。線程
'GEN_CLOSED'
執行結束。調試
def simple_coro2(a): print('-> Started: a =', a) b = yield a #等着賦值b 把a甩出去 print('-> Received: b =', b) c = yield a + b print('-> Received: c =', c) my_coro_2 = simple_coro2(14) from inspect import getgeneratorstate print(getgeneratorstate(my_coro_2)) print(next(my_coro_2)) getgeneratorstate(my_coro_2) print(my_coro_2.send(28)) # 沒有yield 出來 因此沒有返回值 print(my_coro_2.send(99)) getgeneratorstate(my_coro_2)
getgeneratorstate 函數指明,處於 GEN_SUSPENDED 狀態(即協程在 yield 表達式處暫停)。code
❺ 把數字 99 發給暫停的協程;計算 yield 表達式,獲得 99,而後把那個數綁定給 c。
打印 -> Received: c = 99 消息,而後協程終止,致使生成器對象拋出
StopIteration 異常。orm
def averager(): total = 0.0 count = 0 average = None while True: term = yield average total += term count += 1 average = total / count coro_avg = averager() print(next(coro_avg)) print(coro_avg.send(10)) print(coro_avg.send(15)) print(coro_avg.send(20))
調用 next 函數,預激協程。
➊ 這個無限循環代表,只要調用方不斷把值發給這個協程,它就會一直接收值,而後生
成結果。僅當調用方在協程上調用 .close() 方法,或者沒有對協程的引用而被垃圾回收
程序回收時,這個協程纔會終止。
➋ 這裏的 yield 表達式用於暫停執行協程,把結果發給調用方;還用於接收調用方後面
發給協程的值,恢復無限循環。協程
發送某個哨符值,讓協程退出。
內置的 None 和Ellipsis 等常量常常用做哨符值。對象
Ellipsis 的優勢是,數據流中不太常有這個值。
generator.throw(exc_type[, exc_value[, traceback]])
導致生成器在暫停的 yield 表達式處拋出指定的異常。
若是生成器處理了拋出的異常,代碼會向前執行到下一個 yield 表達式,而產出的值會成爲調用 generator.throw方法獲得的返回值。
導致生成器在暫停的 yield 表達式處拋出 GeneratorExit 異常。
若是生成器沒有處理這個異常,或者拋出了 StopIteration 異常(一般是指運行到結尾),調用方不會報錯。
若是收到 GeneratorExit 異常,生成器必定不能產出值,不然解釋器會拋出
RuntimeError 異常。
exc_coro.throw(ZeroDivisionError)
exc_coro.close()
from collections import namedtuple Result = namedtuple('Result', '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 Result(count, average) coro_avg = averager() next(coro_avg) coro_avg.send(30) coro_avg.send(6.5) try: coro_avg.send(None) except StopIteration as exc: result = exc.value print(result)
捕獲 StopIteration 異常,獲取 averager 返回的值
yield from 結構會在內部自動捕獲 StopIteration 異常。
這種處理方式與 for 循環處理 StopIteration 異常的方式同樣:循環機制使用用戶易於理解的方式處理異常。
對 yield from 結構來講,解釋器不只會捕獲 StopIteration 異常,還會把value 屬性的值變成 yield from 表達式的值。
yield from 結構惟一的做用是替代產出值的嵌套 for 循環, 這句話不對
yield from 的主要功能是打開雙向通道,把最外層的調用方與最內層的子生成器鏈接起來,
這樣兩者能夠直接發送和產出值,還能夠直接傳入異常,
而不用在位於中間的協程中添加大量處理異常的樣板代碼。
有了這個結構,協程能夠經過之前不可能的方式委託職責。
委派生成器
包含 yield from <iterable> 表達式的生成器函數。
子生成器
從 yield from 表達式中 <iterable> 部分獲取的生成器。這就是 PEP 380 的標題
(「Syntax for Delegating to a Subgenerator」)中所說的「子生成器」(subgenerator)。
調用方
PEP 380 使用「調用方」這個術語指代調用委派生成器的客戶端代碼。在不一樣的語境
中,我會使用「客戶端」代替「調用方」,以此與委派生成器(也是調用方,由於它調用了子
生成器)區分開。
不使用yield from
from collections import namedtuple Result = namedtuple('Result', '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 Result(count, average) # ➍ data = { 'girls;kg': [40.9, 38.5, 44.3, 42.2, 45.2, 41.7, 44.5, 38.0, 40.6, 44.5], 'girls;m': [1.6, 1.51, 1.4, 1.3, 1.41, 1.39, 1.33, 1.46, 1.45, 1.43], 'boys;kg': [39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3], 'boys;m': [1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46], } def main(data): result = {} for key, values in data.items(): coro_avg = averager() next(coro_avg) for value in values: coro_avg.send(value) try: coro_avg.send(None) except StopIteration as exc: result[key] = exc.value print(result) if __name__ == '__main__': main(data)
- 這裏的try: catch stop異常要一直存在
用yiled from 及委派生成器做用
from collections import namedtuple Result = namedtuple('Result', '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 Result(count, average) # ➍ data = { 'girls;kg': [40.9, 38.5, 44.3, 42.2, 45.2, 41.7, 44.5, 38.0, 40.6, 44.5], 'girls;m': [1.6, 1.51, 1.4, 1.3, 1.41, 1.39, 1.33, 1.46, 1.45, 1.43], 'boys;kg': [39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3], 'boys;m': [1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46], } def grouper(results): while True: res_obj = yield from averager() results.append(res_obj) def main(data): results = [] for key, values in data.items(): coro_avg = grouper(results) next(coro_avg) for value in values: coro_avg.send(value) # 這個None是中止返回 哨兵 coro_avg.send(None) print(results) # report(result) # 輸出報告 def report(results): for key, result in sorted(results.items()): group, unit = key.split(';') print('{:2} {:5} averaging {:.2f}{}'.format( result.count, group, result.average, unit)) if __name__ == '__main__': main(data)
官方的案例
from collections import namedtuple Result = namedtuple('Result', '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 Result(count, average) # ➍ # 委派生成器 def grouper(results, key): # ➎ while True: # ➏ results[key] = yield from averager() # ➐ # 客戶端代碼,即調用方 def main(data): # ➑ results = {} for key, values in data.items(): group = grouper(results, key) # ➒ next(group) # ➓ for value in values: group.send(value) # ⓫ group.send(None) # 重要! #⓬ print(results) # 若是要調試,去掉註釋 # report(results) # 輸出報告 def report(results): for key, result in sorted(results.items()): group, unit = key.split(';') print('{:2} {:5} averaging {:.2f}{}'.format( result.count, group, result.average, unit)) data = { 'girls;kg': [40.9, 38.5, 44.3, 42.2, 45.2, 41.7, 44.5, 38.0, 40.6, 44.5], 'girls;m': [1.6, 1.51, 1.4, 1.3, 1.41, 1.39, 1.33, 1.46, 1.45, 1.43], 'boys;kg': [39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3], 'boys;m': [1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46], } if __name__ == '__main__': main(data)
❸ 相當重要的終止條件。若是不這麼作,使用 yield from 調用這個協程的生成器會永
遠阻塞。
❹ 返回的 Result 會成爲 grouper 函數中 yield from 表達式的值
❻ 這個循環每次迭代時會新建一個 averager 實例;每一個實例都是做爲協程使用的生成
器對象。
❼ grouper 發送的每一個值都會經由 yield from 處理,經過管道傳給 averager 實
例。grouper 會在 yield from 表達式處暫停,等待 averager 實例處理客戶端發來的
值。averager 實例運行完畢後,返回的值綁定到 results[key] 上。while 循環會不斷
建立 averager 實例,處理更多的值。
yield from 結構會在內部自動捕獲 StopIteration 異常。這種處理方
式與 for 循環處理 StopIteration 異常的方式同樣:循環機制使用用戶易於理解的方式
處理異常。對 yield from 結構來講,解釋器不只會捕獲 StopIteration 異常,還會把
value 屬性的值變成 yield from 表達式的值。
這裏先不總結 帶我把協程 程序加進去