1 終止協程和異常處理 2 3 協程中未處理的異常會向上冒泡,傳給 next 函數或 send 方法的調用方(即觸發協程的對象) 4 終止協程的一種方式:發送某個哨符值,讓協程退出。內置的 None 和 5 Ellipsis 等常量經常用作哨符值。 Ellipsis 的優點是,數據流中不太常有這個值。有人把 6 StopIteration 類(類本身,而不是實例,也不拋出)作為哨符值;也就是說, 7 是像這樣使用的: my_coro.send(StopIteration)。從 Python 2.5 開始,客戶代碼可以在 8 生成器對象上調用兩個方法,顯式地把異常發給協程。這兩個方法是 throw 和 close。 9 10 generator.throw(exc_type[, exc_value[, traceback]]) 11 致使生成器在暫停的 yield 表達式處拋出指定的異常。如果生成器處理了拋出的異 12 常,代碼會向前執行到下一個 yield 表達式,而產出的值會成為調用 generator.throw 13 方法得到的返回值。如果生成器沒有處理拋出的異常,異常會向上冒泡,傳到調用方的上 14 下文中。 15 16 generator.close() 17 致使生成器在暫停的 yield 表達式處拋出 GeneratorExit 異常。如果生成器沒有處 18 理這個異常,或者拋出了 StopIteration 異常(通常是指運行到結尾),調用方不會報 19 錯。如果收到 GeneratorExit 異常,生成器一定不能產出值,否則解釋器會拋出 20 RuntimeError 異常。生成器拋出的其他異常會向上冒泡,傳給調用方。 21 22 讓協程返回值 23 24 from collections import namedtuple 25 26 Results = namedtuple('Result', 'count average') 27 28 def coro_average_return(): 29 total = 0.0 30 count = 0 31 average = None 32 while 1: 33 term = yield # 這一版不產出值 34 if term is None: # 發送 None 會終止循環,導致協程結束,返回結果。一如既往,生成器對象會拋出 StopIteration 異常。異常對象的 value 屬性保存着返回的值。 35 break 36 total += term 37 count += 1 38 average = total / count 39 return Results(count, average) 40 41 coro4 = coro_average_return() 42 next(coro4) 43 coro4.send(10) 44 coro4.send(15) 45 coro4.send(3) 46 try: 47 coro4.send(None) 48 except StopIteration as exe: 49 result = exe.value # return 表達式的值會偷偷傳給調用方,賦值給 StopIteration 異常的一個屬性。 50 # 這樣做有點不合常理,但是能保留生成器對象的常規行為——耗盡時拋出 StopIteration 異常。 51 52 獲取協程的返回值雖然要繞個圈子,但這是 PEP 380 定義的方式,當我們意識到這一點之 53 后就說得通了: yield from 結構會在內部自動捕獲 StopIteration 異常。這種處理方 54 式與 for 循環處理 StopIteration 異常的方式一樣:循環機制通過用戶易於理解的方式 55 處理異常。對 yield from 結構來說,解釋器不僅會捕獲 StopIteration 異常,還會把 56 value 屬性的值變成 yield from 表達式的值。但是,我們無法在控制台中使用交互的方 57 式測試這種行為,因為在函數外部使用 yield from(以及 yield)會導致句法出錯。