yield 與 yield from
阿新 • • 發佈:2019-01-08
簡而言之yield from 就是把main裡的send資料 傳入yield處, send(None)的功能近乎於next(w)
def test1(): while True: yield from test2() def test2(): while True: y = yield if y == None: break def main(): w = test1() next(w)for i in range(0, 10): w.send(i) w.send(None)
main()
三者之間的關係圖
委派生成器在 yield from 表示式處暫停時,呼叫方可以直接把資料發給子生成器,子生成器再把產出的值發給呼叫方。子生成器返回之後,直譯器會丟擲StopIteration 異常,並把返回值附加到異常物件上,此時委派生成器會恢復。
grouper 傳送的每個值都會經由 yield from 處理,通過管道傳給 averager 例項。grouper 會在 yield from 表示式處暫停,等待 averager 例項處理客戶端發來的值。averager 例項執行完畢後,返回的值繫結到 results[key] 上。while 迴圈會不斷建立 averager 例項,處理更多的值。
外層 for 迴圈重新迭代時會新建一個 grouper 例項,然後繫結到 group 變數上。前一個 grouper 例項(以及它建立的尚未終止的 averager 子生成器例項)被垃圾回收程式回收。
from collections import namedtuple Result = namedtuple('Result', 'count average') # 子生成器 def averager(): total = 0.0 count = 0 average = None while True: # main 函式傳送資料到這裡print("in averager, before yield") term = yield if term is None: # 終止條件 break total += term count += 1 average = total/count print("in averager, return result") return Result(count, average) # 返回的Result 會成為grouper函式中yield from表示式的值 # 委派生成器 def grouper(results, key): # 這個迴圈每次都會新建一個averager 例項,每個例項都是作為協程使用的生成器物件 while True: print("in grouper, before yield from averager, key is ", key) results[key] = yield from averager() print("in grouper, after yield from, key is ", key) # 呼叫方 def main(data): results = {} for key, values in data.items(): # group 是呼叫grouper函式得到的生成器物件 group = grouper(results, key) print("\ncreate group: ", group) next(group) #預激 group 協程。 print("pre active group ok") for value in values: # 把各個value傳給grouper 傳入的值最終到達averager函式中; # grouper並不知道傳入的是什麼,同時grouper例項在yield from處暫停 print("send to %r value %f now"%(group, value)) group.send(value) # 把None傳入groupper,傳入的值最終到達averager函式中,導致當前例項終止。然後繼續建立下一個例項。 # 如果沒有group.send(None),那麼averager子生成器永遠不會終止,委派生成器也永遠不會在此啟用,也就不會為result[key]賦值 print("send to %r none"%group) group.send(None) print("report result: ") 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, 41, 42, 43, 44, 54], 'girls;m': [1.5, 1.6, 1.8, 1.5, 1.45, 1.6], 'boys;kg':[50, 51, 62, 53, 54, 54], 'boys;m': [1.6, 1.8, 1.8, 1.7, 1.55, 1.6], } if __name__ == '__main__': main(data)