摘要:生成器用于定義生成器函數(shù)只要存在該函數(shù)必定是一個生成器調(diào)用該函數(shù)返回一個生成器讓一個生成器前進使用使一個生成器前進到下一個語句處,并將產(chǎn)出值作為其返回值。
前言
這篇文章大部分來自 David Beazley 在 PyCon 2014 的 PPT 《Generators: The Final Frontier》。這個PPT很長而且非常燒腦,建議在閱讀前應了解 Python 的生成器與攜程相關(guān)知識,推薦《流暢的 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("倒數(shù):", x)
倒數(shù): 10 倒數(shù): 9 倒數(shù): 8 倒數(shù): 7 倒數(shù): 6 倒數(shù): 5 倒數(shù): 4 倒數(shù): 3 倒數(shù): 2 倒數(shù): 1
可以通過 next() 來一步步地讓生成器 yield 一個值,直到函數(shù)迭代器結(jié)束并拋出 StopIteration。如果你對這一頭霧水,建議閱讀《Fluent Python》14.4 章。
這里 for 其實相當于不斷地調(diào)用 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
可以看到,我們?yōu)?seq 里的每項都依次添加了一個 tag。
yield 可以接受傳值yield 的作用是向調(diào)用者返回一個值,調(diào)用者其實也可以向生成器傳值。
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 函數(shù)的返回值是什么呢?
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 右側(cè)的值作為返回值。
生成器 101yield 用于定義生成器函數(shù)
只要 yield 存在該函數(shù)必定是一個生成器
調(diào)用該函數(shù)返回一個生成器
讓一個生成器前進使用 next 使一個生成器前進到下一個 yield 語句處,并將產(chǎn)出值(yielded value)作為其返回值。使用 gen.__next__()效果相同。
注意:這是一個新創(chuàng)建的生成器唯一允許的操作。
def generator(): yield "a" yield "b" gen = generator() # next(gen) "a" # next(gen) "b"給生成器傳值
可以通過調(diào)用生成器的 send 方法來向生成器傳值,這將讓生成器從上次暫停的 yield 前進到下個 yield,并將產(chǎn)出值作為 send 的返回值。
def generator(): item = yield "a" print(item) another_item = yield "b" gen = generator() print(next(gen)) print(gen.send(1))
a 1 b關(guān)閉一個生成器
通過調(diào)用生成器 close 方法可以生成器在 yield 語句處拋出 GeneratorExit。這時僅允許 return,如果沒有捕捉這個錯誤,生成器會靜默關(guān)閉,不拋出錯誤。
def generator(): times = 0 while True: yield times times += 1 gen = generator() print(next(gen)) print(next(gen)) gen.close() # 不會拋出錯誤
0 1拋出錯誤
調(diào)用生成器的 throw 方法可以在 yield 處拋出某個特定類型的錯誤,如果生成器內(nèi)部可以捕捉這個錯誤,那生成器將前進到下個 yield 語句處,并將產(chǎn)出值作為 throw 的返回值,否則中止生成器。
throw 的函數(shù)簽名如下:
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生成器返回值
如果在生成器函數(shù)中加上 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 可以幫你對一個生成器不斷調(diào)用 next 函數(shù),并返回生成器的返回值。言下之意是你可以在生成器里調(diào)用生成器。
def generator(): yield "a" yield "b" return "c" def func(): result = yield from generator() print(result)
調(diào)用 func 結(jié)果是返回一個生成器
# 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總結(jié) 生成器定義
def generator(): ... yield ... return result生成器操作
gen = generator() # 使一個生成器前進 next(gen) # 傳遞一個值 gen.send(item) # 中止生成器 gen.close() # 拋出錯誤 gen.throw(exc, val, tb) # 委托 result = yield from gen
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/41954.html
摘要:一個典型的上下文管理器類如下處理異常正如方法名明確告訴我們的,方法負責進入上下的準備工作,如果有需要可以返回一個值,這個值將會被賦值給中的??偨Y(jié)都是關(guān)于上下文管理器的內(nèi)容,與協(xié)程關(guān)系不大。 Part 1 傳送門 David Beazley 的博客 PPT 下載地址 在 Part 1 我們已經(jīng)介紹了生成器的定義和生成器的操作,現(xiàn)在讓我們開始使用生成器。Part 2 主要描述了如...
摘要:本文先回顧生成器,然后過渡到協(xié)程編程。其作用主要體現(xiàn)在三個方面數(shù)據(jù)生成生產(chǎn)者,通過返回數(shù)據(jù)數(shù)據(jù)消費消費者,消費傳來的數(shù)據(jù)實現(xiàn)協(xié)程。解決回調(diào)地獄的方式主要有兩種和協(xié)程。重點應當關(guān)注控制權(quán)轉(zhuǎn)讓的時機,以及協(xié)程的運作方式。 轉(zhuǎn)載請注明文章出處: https://tlanyan.me/php-review... PHP回顧系列目錄 PHP基礎(chǔ) web請求 cookie web響應 sess...
摘要:源碼之分析的協(xié)程原理分析版本為支持異步,實現(xiàn)了一個協(xié)程庫。提供了回調(diào)函數(shù)注冊當異步事件完成后,調(diào)用注冊的回調(diào)中間結(jié)果保存結(jié)束結(jié)果返回等功能注冊回調(diào)函數(shù),當被解決時,改回調(diào)函數(shù)被調(diào)用。相當于喚醒已經(jīng)處于狀態(tài)的父協(xié)程,通過回調(diào)函數(shù),再執(zhí)行。 tornado 源碼之 coroutine 分析 tornado 的協(xié)程原理分析 版本:4.3.0 為支持異步,tornado 實現(xiàn)了一個協(xié)程庫。 ...
摘要:項目地址我之前翻譯了協(xié)程原理這篇文章之后嘗試用了模式下的協(xié)程進行異步開發(fā),確實感受到協(xié)程所帶來的好處至少是語法上的。 項目地址:https://git.io/pytips 我之前翻譯了Python 3.5 協(xié)程原理這篇文章之后嘗試用了 Tornado + Motor 模式下的協(xié)程進行異步開發(fā),確實感受到協(xié)程所帶來的好處(至少是語法上的:D)。至于協(xié)程的 async/await 語法是如...
摘要:協(xié)程的判斷條件下面我們來著重看下的源碼,因為從這里開始就涉及到協(xié)程的判斷。第二點是關(guān)鍵點,用來判斷該方法的調(diào)用是否使用到了協(xié)程。原理我們先來看下使用協(xié)程是怎么寫的這是一個標準的協(xié)程寫法,然后我們再套用上面的條件,發(fā)現(xiàn)完全匹配不到。 第一眼看,跟我之前印象中的有點區(qū)別(也不知道是什么版本),return的時候居然...
閱讀 2215·2021-11-25 09:43
閱讀 1181·2021-11-23 09:51
閱讀 3514·2021-11-23 09:51
閱讀 3643·2021-11-22 09:34
閱讀 1576·2021-10-09 09:43
閱讀 2138·2019-08-30 15:53
閱讀 3173·2019-08-30 14:07
閱讀 582·2019-08-28 18:14