yield from 句法
在生成器中使用yield from subgen()時,subgen會獲得控制權(quán)逸爵,把產(chǎn)出的值傳給生成器的調(diào)用方,即調(diào)用方可以直接控制subgen。與此同時背捌,gen會阻塞,等待subgen終止洞斯。
【簡單應(yīng)用】用來簡化for循環(huán)中的yield表達式
舉個栗子:
def gen():
for c in 'AB':
yield c
for i in range(1,3):
yield i
print(list(gen()))
前后對比
def gen():
yield from 'AB'
yield from range(1,3)
print(list(gen()))
【解釋】yield from x表達式對x對象所做的第一件事是毡庆,調(diào)用iter(x),從中獲取迭代器烙如。因此么抗,x可以是任何可迭代的對象。
【應(yīng)用升級】把職責(zé)委托給子生成器
yield from 的主要功能是打開雙向通道亚铁,把最外層的調(diào)用方與最內(nèi)層的子生成器連接起來蝇刀,這樣二者可以直接發(fā)送和產(chǎn)出值,還可以直接傳入異常刀闷,而不用在位于中間的協(xié)程中添加大量處理異常的樣板代碼熊泵。通過這個結(jié)構(gòu),協(xié)程可以把功能委托給子生成器甸昏。
主要術(shù)語:
委派生成器:
包含 yield from <iterable> 表達式的生成器函數(shù)顽分。子生成器:
從 yield from 表達式中 <iterable>部分獲取的生成器函數(shù)。調(diào)用方
調(diào)用委派生成器的客戶端(調(diào)用方)代碼施蜜。
graph LR
調(diào)用方 ==> 委派生成器
委派生成器 ==> 子生成器
【店小二】:各位看官卒蘸,下面的代碼請查收@ @
from collections import namedtuple
Result = namedtuple('Result','count average')
# 子生成器
def averager():
total = 0.0
count = 0
average = None
while True:
# main 方法中發(fā)送的各種值,會綁定到term變量上
term = yield
# 子生成器終止的條件
if term is None:
break
total += term
count += 1
average = total / count
# 返回值會成為grouper中 yield from表達式的值
return Result(count,average)
# 委派生成器
def grouper(results,key):
while True:
# 每次迭代都會生成一個averager實例翻默。每個生成器都是本協(xié)程(grouper)使用的生成器對象缸沃。
results[key] = yield from averager()
# 客戶端代碼
def main(data):
results = {}
for key,values in data.items():
# results 用來存儲結(jié)果
group = grouper(results, key)
# 預(yù)激活協(xié)程
next(group)
for value in values:
# 發(fā)送的每個值都會經(jīng)由grouper的yield from處理,通過管道傳給averager實例修械。同時趾牧,當(dāng)前的grouper實例,會在yield from 處暫停肯污。
group.send(value)
# 把None值傳入grouper翘单,導(dǎo)致當(dāng)前的averager實例終止吨枉,并讓grouper繼續(xù)運行,再創(chuàng)建一個aveager實例哄芜,處理下一組值貌亭。
group.send(None)
print(results)
data = {
'girls;kg':[40.9,38.5,44.3,42.2,45.2,41.7,44.5,38.0,40.6,44.5],
'girls;m':[1.6,1.51,1.4,1.3,1.41,1.39,1.33,1.46,1.45,1.43],
'boys;kg':[39.0,40.8,43.2,40.8,43.1,38.6,41.4,40.6,36.3],
'boys;m':[1.38,1.5,1.32,1.25,1.37,1.48,1.25,1.49,1.46]
}
main(data)
Output:
{'girls;kg': Result(count=10, average=42.040000000000006), 'girls;m': Result(count=10, average=1.4279999999999997), 'boys;kg': Result(count=9, average=40.422222222222224), 'boys;m': Result(count=9, average=1.3888888888888888)}
【提示】委派生成器在yield from 表達式處暫停時,調(diào)用方可以直接把數(shù)據(jù)發(fā)給子生成器认臊,子生成器再把產(chǎn)出的值發(fā)給調(diào)用方圃庭。子生成器返回之后,解釋器會拋出StopIteration異常失晴,并把返回值附加到異常對象上剧腻,此時委派生成器會恢復(fù)運行。
【重點】
yield from 句法的注意點
如果子生成器不終止师坎,委派生成器會在yield from處永遠(yuǎn)暫停恕酸。
因為委派生成器相當(dāng)于管道,所以可以把任意數(shù)量個委派生成器連接在一起:一個委派生成器使用yield from調(diào)用一個子生成器胯陋,而那個子生成器本身也是委派生成器蕊温,使用yield from調(diào)用另一個子生成器,以此類推遏乔。最終义矛,這個鏈條要以一個只使用yield表達式的簡單生成器結(jié)束碳柱;不過巡蘸,也能以任何可迭代的對象結(jié)束幌缝。
任何yield from鏈條都必須由客戶驅(qū)動练俐,在最外層委派生成器上調(diào)用next(...)函數(shù)或.send(...)方法。
yield from 句法的意義
【寫在前面】下面的幾點知識...有點復(fù)雜盒揉,考慮的如果不夠全面的話灯谣,可能寫的程序會崩均澳。
子生成器產(chǎn)出的值都直接傳給委派生成器的調(diào)用方(即客戶端代碼)胞谭。
使用send()方法發(fā)給委派生成器的值都直接傳給子生成器垃杖。如果發(fā)送的值是None,那么會調(diào)用子生成器的next()方法丈屹。
如果發(fā)送的值不是None调俘,那么會調(diào)用子生成器的send()方法。如果調(diào)用的方法拋出StopIteration異常旺垒,那么委派生成器恢復(fù)運行彩库。任何其他異常都會向上冒泡,傳給委派生成器先蒋。
生成器退出時骇钦,生成器(或子生成器)中的return expr表達式會觸發(fā)StopIteration(expr)異常拋出。
yield from表達式的值是子生成器終止時傳給StopIteration異常的第一個參數(shù)竞漾。
傳入委派生成器的異常司忱,除了GeneratorExit之外都傳給子生成器的throw()方法皇忿。
如果調(diào)用throw()方法時拋出StopIteration異常,委派生成器恢復(fù)運行坦仍。StopIteration之外的異常會向上冒泡,傳給委派生成器叨襟。
- 如果把GeneratorExit異常傳入委派生成器繁扎,或者在委派生成器上調(diào)用close()方法,那么在子生成器上調(diào)用close()方法糊闽,如果它有的話梳玫。如果調(diào)用close()方法導(dǎo)致異常拋出,那么異常會向上冒泡右犹,傳給委派生成器提澎;否則,委派生成器拋出GeneratorExit異常念链。