yield筆記

看到協(xié)程時對yield的用法總是理解不夠透徹,因此做一些小筆記横浑,方便日后查看剔桨。
此處以一個小例子來說明send到底是干嘛用的,例子來自Python協(xié)程:從yield/send到async/await:

1徙融、起源:簡單的yield生成器

def fib(n):
    index = a = 0
    b = 1
    while index<n:
        yield b
        a, b = b, a+b
        index += 1

#簡單調(diào)用:
for i in fib(5):
    print(i,end=' ',sep=',')
# 1 1 2 3 5

#相當于
f = fib(5)
for j in range(5):
    print(next(f),end=' ')

在上面的例子中洒缀,函數(shù)fib(n)相當于一個生成器,for循環(huán)每一次調(diào)用相當于執(zhí)行一次next(),最后調(diào)用完會遇到StopIteration退出for循環(huán)欺冀。

2树绩、send是什么?

想要了解send()是什么隐轩,就不得不先理解yield表達式了饺饭。yield表達式可表示為[res =] yield [expression],從某種程度來說,方括號中的值都是可以省略的职车,例如若將fib(n)函數(shù)的yield n改成yield也不會報錯瘫俊,只是這樣fib(5)這個生成器就會返回5個None了鹊杖,而將yield n改成 s = yield n結(jié)果則不會變,只是此時可以通過sendyield表達式傳入數(shù)值扛芽,這個數(shù)值即賦值給了s骂蓖。看看代碼更加清晰:

# yield b ==> yield
def fib(n):
    index = a = 0
    b = 1
    while index<n:
        yield
        a, b = b, a+b
        index += 1

for i in fib(5):
    print(i,end=' ')
# None None None None None

# yield b ==> s=yield b
def fib(n):
    index = a = 0
    b = 1
    while index<n:
        s = yield b
        a, b = b, a+b
        index += 1
# 1 1 2 3 5

既然使用了yield表達式后對生成器沒有改變川尖,那么他有什么作用呢登下?要想yield表達式發(fā)揮作用,就必須使用send對其進行傳值(賦值)叮喳,利用傳入的值可以來實現(xiàn)一些有用的功能庐船,例如下面簡單的記錄一下日志信息:

import datetime
import time
import random


def fib(n):
    index = a = 0
    b = 1
    while index < n:
        now = yield b
        print(now)
        a, b = b, a+b
        index += 1

f = fib(5)
res = next(f)   #這一步是必須的,此處相當于send(None)嘲更,在fib函數(shù)中此時執(zhí)行到y(tǒng)ield產(chǎn)出值b=1(也即res等于1),并掛起等待send傳入值
while True:
    try:
        print(res)
        time.sleep(random.random())
        res = f.send(datetime.datetime.now())
    except:
        print('over')
        break
#輸出
1
2017-12-21 14:15:49.296765
1
2017-12-21 14:15:49.583339
2
2017-12-21 14:15:50.492255
3
2017-12-21 14:15:51.006442
5
2017-12-21 14:15:51.113537
over

從上面可以看出,send發(fā)送的值都賦值給了yield表達式的左邊的now變量了揩瞪。另外值得注意的一點是赋朦,在使用send之前必須先調(diào)用一次next(),此處的next相當于send(None)
yield表達式的執(zhí)行順序是先yield產(chǎn)生值李破,然后掛起等待send傳入值宠哄。也因此輸出的結(jié)果是先輸出fib序列,然后在輸出傳入值相關(guān)的信息嗤攻。
下面的例子更好的說明了執(zhí)行步驟毛嫉,為了更好的說明執(zhí)行順序,此處將fib序列的第一個值改成了2:


import datetime
import time
import random


def fib(n):
    index = 0
    a = 2
    b = 3
    while index < n:
        now = yield b
        print(now)
        a, b = b, a+b
        index += 1

f = fib(5)
res = next(f)
n = 1
while n < 2:
    try:
        print(res)
        time.sleep(random.random())
        res = f.send(datetime.datetime.now())
        print(res)
    except:
        print('over')
        break
    n += 1
#此時僅執(zhí)行了一次send妇菱,輸出如下
3
2017-12-21 21:23:06.106728
5

上面的兩個不同的fib生成結(jié)果中第一個3是在預(yù)激活協(xié)程時yield產(chǎn)生的,在yield表達式右邊產(chǎn)生值后承粤,便會掛起等待傳入?yún)?shù)并賦值給左側(cè)的變量now。隨后send將時間傳入賦值給了yield 表達式左邊的now闯团,now被賦值后會一直執(zhí)行到再次yield b生成5辛臊,這也是為什么下面的res是5。此時yield 表達式又再次執(zhí)行到了yield并掛起等待給now賦值(send)的時候房交。如此循環(huán)彻舰,直到yield表達式右側(cè)(這里的右側(cè)依舊是一個生成器)的值耗盡候味,這是再次send時會引發(fā)生StopIteration

3、yield from 是何方神圣?

說完了sendyield from又是用來干什么的呢?下面這個例子也許可以對yield from的用法做一些最簡單的說明:

def f1():
    for  i in range(5):
        yield i
    for j in 'abc':
        yield j

def f2():
    yield from f1()

# 下面兩種調(diào)用生成器的結(jié)果是一致的
for i in f1():
    print(i,end=' ')
for j in f2():
    print(j,end=' ')
# 0 1 2 3 4 a b c

但是yield from的作用僅僅如此嗎眉枕?
你見過有返回值的生成器嗎姥宝?下面這個生成器在終止時(觸發(fā)StopIteration)會返回一個值碳蛋。

def func():
    index = 0
    res = 111
    while index < 5:
        s = yield  # ④
        print('s: ', s)  
        index += 1
    return res

def delegate():
    res = yield from func()  # ③         ##⑥
    print('res: ', res)

f = delegate()
f.send(None)     # ①
i = 10
while i < 15:
    try:
        f.send(i)  # ② 此處send的值發(fā)送到了子生成器func()中
    except:
        pass
    i += 1
#輸出
s:  10
s:  11
s:  12
s:  13
s:  14
res:  111  

從輸出結(jié)果可以看出3件事:
首先,委派生成器delegate的確從yield from中收到了返回值——res=111,而且這個返回值并非yield的值比肄,而是子生成器函數(shù)func的返回值掀亥。
其次撮竿,從輸出結(jié)果可以看出,send發(fā)送的值都傳到了子生成器中惨驶,也即是從委派生成器delegate傳到了yield from表達式中的子生成器func中续扔,這也是輸出結(jié)果index 10 ... index 14的由來识脆。
最后艘包,委派生成器向子生成器send發(fā)送值后胡诗,自身會被掛起,直到子生成器函數(shù)func觸發(fā)終止異常(StopIteration)返回值震庭,這個返回值賦值給yield from表達式左邊的res瑰抵,然后委派生成器就會繼續(xù)執(zhí)行,這也是為什么器联,res:111會在最后輸出二汛。當我們將while i<15改成while i<12后會看到輸出結(jié)果沒有res輸出,這是因為生成器還沒有迭代完(while index<5這里需要send5次才會觸發(fā)異常返回值)拨拓,還在等待send發(fā)送值肴颊。

現(xiàn)在,讓我們梳理下上面代碼的執(zhí)行順序:
①預(yù)激活子生成器func渣磷,此時子生成器在等待傳值婿着;
②向委派生成器delegate傳值(send);
③委派生成器通過yield from表達式向子生成器func中傳(send)值醋界;
④子生成器收到傳入的值i后竟宋,便會向后執(zhí)行print('s: ', s)index+=1yield產(chǎn)生值(雖然此處沒有生成任何值)形纺,最后又回到繼續(xù)等待傳值(send)的狀態(tài)丘侠;
⑤代碼中沒有⑤因為⑤代表著循環(huán)傳值這個過程;
終止生成器逐样,這一步非常關(guān)鍵蜗字,因為每傳入(send)一個值后都會執(zhí)行index+=1,回到等待傳值得狀態(tài)脂新。因此第一次傳值后index=1(需要注意的是預(yù)激活時index為0)秽澳,第四次傳值(send(13))后,此時index=4戏羽,當?shù)谖宕蝹髦禃rindex=5此時會觸發(fā)異常退出while循環(huán)担神,使得子生成器func返回res值,func返回的值又通過yield from表達式賦值給委派生成器的res始花,res收到值后委派生成器終于不再掛起妄讯,向下執(zhí)行孩锡,print(res)。完結(jié)撒花亥贸。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末躬窜,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子炕置,更是在濱河造成了極大的恐慌荣挨,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件朴摊,死亡現(xiàn)場離奇詭異默垄,居然都是意外死亡,警方通過查閱死者的電腦和手機甚纲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進店門口锭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人介杆,你說我怎么就攤上這事鹃操√鲜唬” “怎么了橱夭?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長鹤树。 經(jīng)常有香客問我赴背,道長椰拒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任癞尚,我火速辦了婚禮,結(jié)果婚禮上乱陡,老公的妹妹穿的比我還像新娘浇揩。我一直安慰自己,他們只是感情好憨颠,可當我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布胳徽。 她就那樣靜靜地躺著,像睡著了一般爽彤。 火紅的嫁衣襯著肌膚如雪养盗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天适篙,我揣著相機與錄音往核,去河邊找鬼。 笑死嚷节,一個胖子當著我的面吹牛聂儒,可吹牛的內(nèi)容都是我干的虎锚。 我是一名探鬼主播,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼衩婚,長吁一口氣:“原來是場噩夢啊……” “哼窜护!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起非春,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤柱徙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后奇昙,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體护侮,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年敬矩,在試婚紗的時候發(fā)現(xiàn)自己被綠了概行。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡弧岳,死狀恐怖凳忙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情禽炬,我是刑警寧澤涧卵,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布,位于F島的核電站腹尖,受9級特大地震影響柳恐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜热幔,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一乐设、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧绎巨,春花似錦近尚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至和媳,卻和暖如春格遭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背留瞳。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工拒迅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓坪它,卻偏偏與公主長得像骤竹,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子往毡,可洞房花燭夜當晚...
    茶點故事閱讀 43,728評論 2 351

推薦閱讀更多精彩內(nèi)容