定義:協程是指一個過程,這個過程與調用方協做,產出由調用方提供的值。(協程中一定含有一條yield語句)python
協程與生成器相似,都是定義體內包含yield關鍵字的函數。不過,在協程中,yield一般出如今表達式的右邊(例如,data = yield),能夠產出值,也能夠不產出。函數
協程只能處於這四個狀態中的一個,當前狀態能夠由 inspect.getgeneratorstate(...)函數獲取debug
由於send() 方法的參數會成爲暫停的yield表達式的值,因此,僅當協程處於暫停狀態時才能調用send()方法code
協程須要被預激,預激是經過next()函數進行orm
給協程添加預激裝飾器 functools.wraps(),能夠省去協程的預激過程。協程
在生成器gen中使用yield from subgen()時,subgen()會獲得當前的控制權,把產出的值傳給gen的調用方,即調用方能夠直接跳過gen控制subgen。當subgen獲得控制權時,gen會阻塞,同時等待subgen終止。get
一個小例子:generator
def chain(*iters): for iter in iters: yield from iter lst_1 = 'abc' lst_2 = '987' print(list(chain(lst_1, lst_2)))
運行結果:it
['a', 'b', 'c', '9', '8', '7']
這個例子還能夠改寫爲:io
def chain(): yield from 'abc' yield from '987'
輸出結果是同樣的。
yield from 的主要功能是打開雙向通道,把最外層的調用方與最內層的子生成器鏈接起來,這樣,兩者能夠直接發送和產生值,甚至能夠直接傳入異常。
一個複雜的例子,計算中學生的平均身高和體重:
from collections import namedtuple Result = namedtuple('Result', 'count average') # 子生成器 def averager(): # <1> total = 0.0 count = 0 average = None while True: term = yield # <2> if term is None: # <3> break total += term count += 1 average = total/count return Result(count, average) # <4> # 委派生成器 def grouper(results, key): # <5> while True: # <6> results[key] = yield from averager() # <7> # 客戶端代碼,即調用端 def main(data): # <8> results = {} for key, values in data.items(): group = grouper(results, key) # <9> next(group) # <10> for value in values: group.send(value) # <11> group.send(None) # important! <12> # print(results) # uncomment to debug 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)
運行結果:
9 boys averaging 40.42kg 9 boys averaging 1.39m 10 girls averaging 42.04kg 10 girls averaging 1.43m
生成器中都有一個無限循環 while True: 這個無限循環代表,只要調用方不斷把值發送給這個協程,它就會一直接收值,而後生成結果。該循環結束條件:
generator.close()
該方法導致生成器在暫停的 yield 表達式處拋出 GeneratorExit 異常。