Python: generator, yield, yield from 详解
1.Generator Expressions
生成器表達式是用小括號表示的簡單生成器標(biāo)記法:
generator_expression ::= "(" expression comp_for ")"
生成器表達式產(chǎn)生一個生成器對象,它的語法和for類似,出了它是被“()”包含,而不是[]或{};
生成器表達式中變量的計算被延遲到__next__()函數(shù)的調(diào)用,然而最左邊f(xié)or循環(huán)子句被立即計算,這樣,如果他有錯誤的話可以被立即看到。后面的for循環(huán)子句不能被立即計算,因為他們可能依賴于前面的for循環(huán),例如(x*y?for?x?in?range(10)?for?y?in?bar(x))
python3.6以后,如果生成器出現(xiàn)在async?def function中,那么async for子句和await表達式可以被理解為是異步的。如果生成器表達式包含async for子句或者await表達式,就叫做異步生成器表達式。異步生成器表達式產(chǎn)生一個新的異步生成器對象,它是一個異步迭代器。
?
2. Yield Expressions
yield_atom ::= "(" yield_expression ")" yield_expression ::= "yield" [expression_list | "from" expression]Yield表達式用于定義一個生成器函數(shù)或異步生成器函數(shù),因此只能被用于一個函數(shù)定義體內(nèi)。在一個函數(shù)定義體中使用yield表達式使其成為生成器,
在一個async def函數(shù)體內(nèi)使用yield表達式使協(xié)程函數(shù)成為一個異步生成器。例如:
def gen(): //定義一個生成器函數(shù)
yield 123
async def agen(): //定義一個異步生成器函數(shù)
yield 123
當(dāng)一個生成器函數(shù)被調(diào)用,它返回一個迭代器,也叫生成器。這個生成器控制生成器函數(shù)的執(zhí)行。當(dāng)生成器的一個函數(shù)被調(diào)用的時候,生成器函數(shù)開始執(zhí)行。
執(zhí)行到第一個yield表達式時,掛起,返回expression_list的值給調(diào)用者。對于掛起,我們指的是所有局部狀態(tài)被保留,包含當(dāng)前局部變量的綁定,
指令指針,內(nèi)部調(diào)用棧,任何異常處理狀態(tài)。當(dāng)通過調(diào)用生成器的一個函數(shù)恢復(fù)執(zhí)行時,函數(shù)的執(zhí)行就好像是從外部再次調(diào)用yield表達式一樣。恢復(fù)執(zhí)行后,
yield表達式的值取決調(diào)用的方法。如果是__next__()被調(diào)用(一般通過for循環(huán)或者內(nèi)置的next()函數(shù)),那么值是None。如果是send()被調(diào)用,
值是傳給send的參數(shù)的值。
所有的這些使得生成器函數(shù)非常像協(xié)程;它產(chǎn)生多次值,有多個入口點并且執(zhí)行可以被掛起。唯一的不同是,生成器函數(shù)yield后不能控制程序從哪里繼續(xù)執(zhí)行,
控制權(quán)總是傳回給生成器的調(diào)用者。
yield表達式可以在try塊的任何地方,如果生成器在被結(jié)束(到達零引用或者因為垃圾回收機制)之前沒有被恢復(fù)執(zhí)行,生成器的close函數(shù)被調(diào)用,因此finally
子句被執(zhí)行。
當(dāng)yield from <expr> 被使用,它把附加的表達式當(dāng)成一個子迭代器,所有子迭代器產(chǎn)生的值被直接傳回給當(dāng)前生成器函數(shù)的調(diào)用者。當(dāng)前生成器調(diào)用send的參數(shù)值
和調(diào)用throw的異常參數(shù) 都將被傳給底層迭代器(子迭代器),如果他有對應(yīng)的方法的話。否則,send導(dǎo)致AttributeError或者TypeError,而throw立即
raise傳給他的異常。
當(dāng)?shù)讓拥鲌?zhí)行完成,StopIteration對象的value值變成這個yield from表達式的值。這個值可以被顯式的設(shè)置當(dāng)產(chǎn)生StopIteration時,或者自動設(shè)置,如果
子迭代器是一個生成器(子生成器返回一個值) 3. 生成器-迭代器 方法
這部分介紹生成器迭代器的方法,他們可以被用來控制生成器函數(shù)的執(zhí)行。當(dāng)生成器正在執(zhí)行時調(diào)用這些函數(shù)將導(dǎo)致ValueError。
(1)generator.__next__()
開始生成器函數(shù)的執(zhí)行或者從最后被執(zhí)行的yield表達式中恢復(fù)執(zhí)行,如果是恢復(fù)執(zhí)行,yield表達式的值是None,繼續(xù)執(zhí)行到下一個yield表達式,
掛起,返回expression_list的值給__next__()的調(diào)用者。如果生成器沒有再yield一個值,則產(chǎn)生一個StopIteration異常
這個方法一般被隱式調(diào)用,例如for循環(huán),next()
(2)generator.send(value)
恢復(fù)執(zhí)行并且發(fā)送一個值到生成器函數(shù)。這個value參數(shù)就是當(dāng)前yield表達式的結(jié)果。send方法返回下一個生成器yield的值
如果沒有yield,返回StopIteration。當(dāng)用send來啟動生成器,他的參數(shù)必須是None,因為沒有yield表達式接收值。
(3)generator.throw(type[,value[,traceback]])
在生成器掛起的地方產(chǎn)生一個type類型的異常,并且返回下一個生成器函數(shù)yield的值,如果沒有yield,返回StopIteration。
如果生成器函數(shù)沒有捕獲這個傳進去的異常 ,或者產(chǎn)生了另一個不同的異常,那么將這個異常傳遞給調(diào)用者。
(4)generator.close()
在生成器掛起的地方產(chǎn)生一個GeneratorExit(),如果生成器之后優(yōu)雅的退出,已經(jīng)關(guān)閉,或者產(chǎn)生了一個GeneartorExit(不捕獲該異常),close將返回到它的
調(diào)用者。如果生成器yield一個值,那么產(chǎn)生一個RuntimeError。如果生成器產(chǎn)生了任何其他異常,將傳遞給調(diào)用者。如果
生成器因為一個異常或者正常退出,那么close不做任何事情。
實例: >>> def echo(value=None): ... print("Execution starts when 'next()' is called for the first time.") ... try: ... while True: ... try: ... value = (yield value) ... except Exception as e: ... value = e ... finally: ... print("Don't forget to clean up when 'close()' is called.") ... >>> generator = echo(1) >>> print(next(generator)) Execution starts when 'next()' is called for the first time. 1 >>> print(next(generator)) None >>> print(generator.send(2)) 2 >>> generator.throw(TypeError, "spam") TypeError('spam',) >>> generator.close() Don't forget to clean up when 'close()' is called.
PEP380 加入了yield from表達式,允許一個生成器委派部分操作給另一個生成器。這可以剔除一部分包含yield的代碼放到另一個生成器。另外,
子生成器可以返回一個值,這個值對于委托生成器也是可用的。
雖然主要涉及用來委派一個子生成器,但是yield from 表達式事實上可以委派任何的子迭代器。
低于簡單的迭代器, yield from iterable 本質(zhì)上就是一個簡短的形式:for item in iterable: yield item,例如:
>>> def g(x): ... yield from range(x, 0, -1) ... yield from range(x) ... >>> list(g(5)) [5, 4, 3, 2, 1, 0, 1, 2, 3, 4]
然而,不像普通的循環(huán),yield from 允許子生成器直接從調(diào)用區(qū)域接收send和throw的值,并且返回一個最后的值給外層生成器。示例如下: >>> def accumulate(): ... tally = 0 ... while 1: ... next = yield ... if next is None: ... return tally ... tally += next ... >>> def gather_tallies(tallies): ... while 1: ... tally = yield from accumulate() ... tallies.append(tally) ... >>> tallies = [] >>> acc = gather_tallies(tallies) >>> next(acc) # Ensure the accumulator is ready to accept values >>> for i in range(4): ... acc.send(i) ... >>> acc.send(None) # Finish the first tally >>> for i in range(5): ... acc.send(i) ... >>> acc.send(None) # Finish the second tally >>> tallies [6, 10] ?
?
轉(zhuǎn)載于:https://www.cnblogs.com/programmer-wfq/p/7173580.html
總結(jié)
以上是生活随笔為你收集整理的Python: generator, yield, yield from 详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 树状多层级菜单展示
- 下一篇: 《零基础入门学习Python》学习过程笔