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