摘要:生成器用于定義生成器函數只要存在該函數必定是一個生成器調用該函數返回一個生成器讓一個生成器前進使用使一個生成器前進到下一個語句處,并將產出值作為其返回值。
前言
這篇文章大部分來自 David Beazley 在 PyCon 2014 的 PPT 《Generators: The Final Frontier》。這個PPT很長而且非常燒腦,建議在閱讀前應了解 Python 的生成器與攜程相關知識,推薦《流暢的 Python》。
David Beazley 的博客
PPT 下載鏈接
生成器(generator)使用 yield 來定義一個生成器
def countdown(n): while n > 0: yield n n -= 1 c = countdown(10) c
生成器可用于迭代
for x in countdown(10): print("倒數:", x)
倒數: 10 倒數: 9 倒數: 8 倒數: 7 倒數: 6 倒數: 5 倒數: 4 倒數: 3 倒數: 2 倒數: 1
可以通過 next() 來一步步地讓生成器 yield 一個值,直到函數迭代器結束并拋出 StopIteration。如果你對這一頭霧水,建議閱讀《Fluent Python》14.4 章。
這里 for 其實相當于不斷地調用 next 并處理 StopIteration
c = countdown(3) # next(c) 3 # next(c) 2 # next(c) 1把生成器當作管道
你可以嵌套生成器,這會導致類似于 Unix 命令行管道的效果
def add_A(seq): for item in seq: yield item + "-A" def add_B(seq): for item in seq: yield item + "-B" def add_C(seq): for item in seq: yield item + "-C" seq = ["apple", "banana", "orange"] stacked_generator = add_C(add_B(add_A(seq))) for item in stacked_generator: print(item)
apple-A-B-C banana-A-B-C orange-A-B-C
可以看到,我們為 seq 里的每項都依次添加了一個 tag。
yield 可以接受傳值yield 的作用是向調用者返回一個值,調用者其實也可以向生成器傳值。
def receiver(): while True: received_item = yield print("收到:", received_item) def caller(): recv = receiver() next(recv) # 使生成器前進到 yield for i in "abcd": recv.send(i) caller()
收到: a 收到: b 收到: c 收到: d
那 send 函數的返回值是什么呢?
def receiver(): call_times = 0 while True: item = yield call_times print("收到:", item) call_times += 1 def caller(): recv = receiver() next(recv) for i in "abcd": ret_value = recv.send(i) print("返回值: ", ret_value) caller()
收到: a 返回值: 1 收到: b 返回值: 2 收到: c 返回值: 3 收到: d 返回值: 4
所以 send 可以向生成器傳值的同時會讓生成器前進到下一個 yield 語句,并將 yield 右側的值作為返回值。
生成器 101yield 用于定義生成器函數
只要 yield 存在該函數必定是一個生成器
調用該函數返回一個生成器
讓一個生成器前進使用 next 使一個生成器前進到下一個 yield 語句處,并將產出值(yielded value)作為其返回值。使用 gen.__next__()效果相同。
注意:這是一個新創建的生成器唯一允許的操作。
def generator(): yield "a" yield "b" gen = generator() # next(gen) "a" # next(gen) "b"給生成器傳值
可以通過調用生成器的 send 方法來向生成器傳值,這將讓生成器從上次暫停的 yield 前進到下個 yield,并將產出值作為 send 的返回值。
def generator(): item = yield "a" print(item) another_item = yield "b" gen = generator() print(next(gen)) print(gen.send(1))
a 1 b關閉一個生成器
通過調用生成器 close 方法可以生成器在 yield 語句處拋出 GeneratorExit。這時僅允許 return,如果沒有捕捉這個錯誤,生成器會靜默關閉,不拋出錯誤。
def generator(): times = 0 while True: yield times times += 1 gen = generator() print(next(gen)) print(next(gen)) gen.close() # 不會拋出錯誤
0 1拋出錯誤
調用生成器的 throw 方法可以在 yield 處拋出某個特定類型的錯誤,如果生成器內部可以捕捉這個錯誤,那生成器將前進到下個 yield 語句處,并將產出值作為 throw 的返回值,否則中止生成器。
throw 的函數簽名如下:
throw(typ, [,val, [,tb]])
其中 tyb 是某錯誤類型,val是錯誤信息,tb 為 traceback。更多信息可以參考官方的PEP0342
def generator(): try: yield "apple" except RuntimeError as e: print("捕捉到:", e) yield "banana" gen = generator() print(next(gen)) print(gen.throw(RuntimeError, "運行錯誤"))
apple 捕捉到: 運行錯誤 banana生成器返回值
如果在生成器函數中加上 return 那在運行到 return 時將會把返回值作為 StopIteration 的值傳遞出去。這個是 Python3 的特性,Python2 生成器不能返回某個值。
def generator(): yield return "apple" g = generator() next(g) try: next(g) except StopIteration as e: print(e)
apple生成器委托
使用 yield from 可以幫你對一個生成器不斷調用 next 函數,并返回生成器的返回值。言下之意是你可以在生成器里調用生成器。
def generator(): yield "a" yield "b" return "c" def func(): result = yield from generator() print(result)
調用 func 結果是返回一個生成器
# func()# next(func()) "a"
另外一個例子
def chain(x, y): yield from x yield from y a = [1, 2, 3] b = [4, 5, 6] for i in chain(a, b): print(i, end=" ")
1 2 3 4 5 6
c = [7, 8, 9] for i in chain(a, chain(b, c)): print(i, end=" ")
1 2 3 4 5 6 7 8 9Part 1總結 生成器定義
def generator(): ... yield ... return result生成器操作
gen = generator() # 使一個生成器前進 next(gen) # 傳遞一個值 gen.send(item) # 中止生成器 gen.close() # 拋出錯誤 gen.throw(exc, val, tb) # 委托 result = yield from gen
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/41954.html
摘要:一個典型的上下文管理器類如下處理異常正如方法名明確告訴我們的,方法負責進入上下的準備工作,如果有需要可以返回一個值,這個值將會被賦值給中的。總結都是關于上下文管理器的內容,與協程關系不大。 Part 1 傳送門 David Beazley 的博客 PPT 下載地址 在 Part 1 我們已經介紹了生成器的定義和生成器的操作,現在讓我們開始使用生成器。Part 2 主要描述了如...
摘要:源碼之分析的協程原理分析版本為支持異步,實現了一個協程庫。提供了回調函數注冊當異步事件完成后,調用注冊的回調中間結果保存結束結果返回等功能注冊回調函數,當被解決時,改回調函數被調用。相當于喚醒已經處于狀態的父協程,通過回調函數,再執行。 tornado 源碼之 coroutine 分析 tornado 的協程原理分析 版本:4.3.0 為支持異步,tornado 實現了一個協程庫。 ...
摘要:項目地址我之前翻譯了協程原理這篇文章之后嘗試用了模式下的協程進行異步開發,確實感受到協程所帶來的好處至少是語法上的。 項目地址:https://git.io/pytips 我之前翻譯了Python 3.5 協程原理這篇文章之后嘗試用了 Tornado + Motor 模式下的協程進行異步開發,確實感受到協程所帶來的好處(至少是語法上的:D)。至于協程的 async/await 語法是如...
摘要:協程的判斷條件下面我們來著重看下的源碼,因為從這里開始就涉及到協程的判斷。第二點是關鍵點,用來判斷該方法的調用是否使用到了協程。原理我們先來看下使用協程是怎么寫的這是一個標準的協程寫法,然后我們再套用上面的條件,發現完全匹配不到。 第一眼看,跟我之前印象中的有點區別(也不知道是什么版本),return的時候居然...
閱讀 2205·2021-11-25 09:43
閱讀 1172·2021-11-23 09:51
閱讀 3506·2021-11-23 09:51
閱讀 3633·2021-11-22 09:34
閱讀 1567·2021-10-09 09:43
閱讀 2129·2019-08-30 15:53
閱讀 3169·2019-08-30 14:07
閱讀 577·2019-08-28 18:14