Python yield使用詳解(一)

生成器

yield語句可以作為生成器

def countdown(n):
    while n > 0:
        yield n
        n -= 1
        
# 可以當?shù)鱽硎褂盟?for x in countdown(10):
    print('T-minus', x)
    
# 可以使用next()來產(chǎn)出值椿息,當生成器函數(shù)return(結(jié)束)時汛聚,報錯赐稽。
>>> c = countdown(3)
>>> c
<generator object countdown at 0x10064f900>
>>> next(c)
3
>>> next(c)
2
>>> next(c)
1
>>> next(c)
Traceback (most recent call last):
 File "<stdin>", line 1, in ?
StopIteration
>>>

這篇文章我著重講yield作為協(xié)程的使用方法塑荒,作為生成器的話我一筆帶過忽妒,想要仔細了解迭代器生成器使用玩裙,我這里推薦個教程。完全理解Python迭代對象段直、迭代器吃溅、生成器 ,很棒鸯檬,還有的話就是與生成器密切相關的itertools模塊决侈,可以了解下。但是我在講yield協(xié)程之前我再給出一張圖來說yield一個有趣的用法喧务。

生成器類似于UNIX管道的作用

這個process會有難以置信的作用赖歌,比如實現(xiàn)UNIX中grep的作用枉圃。不展開,以后肯定會用到它庐冯。

生成器進化為協(xié)程

一個協(xié)程例子

重頭戲來了孽亲。
如果你想更多的使用yield,那么就是協(xié)程了展父。協(xié)程就不僅僅是產(chǎn)出值了墨林,而是能消費發(fā)送給它的值。
那么這里的例子就用協(xié)程實現(xiàn)上面的UNIX的grep作用

def grep(pattern):
    print("Looking for {}".format(pattern))
    while True:
        line = yield
        if pattern in line:
            print('{} : grep success '.format(line))
            
>>> g=grep('python')
# 還是個生成器
>>> g
<generator object grep at 0x7f17e86f3780>  
# 激活協(xié)程犯祠!只能用一次,也可以用g.send(None)來代替next(g)
>>> next(g)
Looking for python
# 使用.send(...)發(fā)送數(shù)據(jù)酌呆,發(fā)送的數(shù)據(jù)會成為生成器函數(shù)中yield表達式值衡载,即變量line的值
>>> g.send("Yeah, but no, but yeah, but no")

>>> g.send("A series of tubes")
# 協(xié)程,協(xié)程隙袁,就是互相協(xié)作的程序痰娱,我發(fā)數(shù)據(jù)過去然后你協(xié)助我一下看看grep成功沒
>>> g.send("python generators rock!")
python generators rock! : grep success 
# 關閉
>>> g.close()

例子講完了。有幾個注意點:

  • 生成器用于生成供迭代的數(shù)據(jù)
  • 協(xié)程是數(shù)據(jù)的消費者
  • 為了避免腦袋炸裂菩收,不能把兩個概念混為一談
  • 協(xié)程與迭代無關
  • 注意梨睁,雖然在協(xié)程值會使用yield產(chǎn)出值,但這與迭代無關

發(fā)送數(shù)據(jù)給協(xié)程

預激活娜饵,到y(tǒng)ield處暫停坡贺。然后發(fā)送item值,協(xié)程繼續(xù)了箱舞,協(xié)程中item接收到發(fā)送的那個值遍坟,然后到下一個yield再暫停。

使用一個裝飾器

如果不預激(primer),那么協(xié)程沒什么用晴股,調(diào)用g.send(x)之前愿伴。記住一定要調(diào)用next(g)。為了簡化協(xié)程用法电湘,有時會使用一個預激裝飾器隔节,如下。

def coroutine(func):
    def primer(*args,**kwargs):
        cr = func(*args,**kwargs)
        next(cr)
        return cr
    return primer
    
@coroutine
def grep(pattern):
 ...  

關閉一個協(xié)程

  • 一個協(xié)程有可能永遠運行下去
  • 可以 .close()讓它停下來
    例子中已經(jīng)體現(xiàn)寂呛,不展開怎诫。

捕捉close()

def grep(pattern):
    print("Looking for {}".format(pattern))
    try:
        while True:
            line = yield
            if pattern in line:
                print(line)
    except GeneratorExit:
        print("Going away. Goodbye")

捕捉到.close()方法,然后會打印"Going away. Goodbye"昧谊。

拋出異常

>>> g = grep("python")
>>> next(g) # Prime it
Looking for python
>>> g.send("python generators rock!")
python generators rock! : grep success 
>>> g.throw(RuntimeError,"You're hosed")
Traceback (most recent call last):
.....
.....
RuntimeError: You're hosed
>>>

說明:

  • 在協(xié)程內(nèi)部能拋出一個異常
  • 異常發(fā)生于yield表達式
  • 不慌刽虹,我們可以平常的方法處理它

生成器返回數(shù)值

鑒于上面的例子是一直run下去的,所以稍加修改:

def grep(pattern):
    print("Looking for {}".format(pattern))
    while True:
        line = yield
        # 當發(fā)送的數(shù)據(jù)為None時呢诬,跳出while循環(huán)
        if line is None:
            break
        else:
            if pattern in line:
                print('{} : grep success '.format(line))
    return 'End'
    
>>> ..... 省略
>>> g.send(None)
Traceback (most recent call last):
 ...
 ...
StopIteration: End
  
# 這里可以用try捕捉異常涌哲,異常對象的value屬性保存著返回的值
    try:
        g.send(None)
    except StopIteration as exc:
        result = exc.value
                   
>>> result
End

圖解如下


說明:

  • 通過捕捉異常獲取返回值
  • 只支持python3

總結(jié)

  1. yield的基本用法已經(jīng)差不多了胖缤,有兩個方面:生成器與協(xié)程(理解協(xié)程的關鍵在于明白它在何處暫停發(fā)送出的數(shù)據(jù)傳到了哪個變量)
  2. yield的另一方面的應用是上下文管理器下一節(jié)講
  3. yield from我這里暫時不講,留到后面阀圾。yield from會在內(nèi)部自動捕獲StopIteration異常等

參考資料

David beazley協(xié)程
Fluent Python

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末哪廓,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子初烘,更是在濱河造成了極大的恐慌涡真,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件肾筐,死亡現(xiàn)場離奇詭異哆料,居然都是意外死亡,警方通過查閱死者的電腦和手機吗铐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門东亦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人唬渗,你說我怎么就攤上這事典阵。” “怎么了镊逝?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵壮啊,是天一觀的道長。 經(jīng)常有香客問我撑蒜,道長歹啼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任减江,我火速辦了婚禮染突,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘辈灼。我一直安慰自己份企,他們只是感情好,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布巡莹。 她就那樣靜靜地躺著司志,像睡著了一般。 火紅的嫁衣襯著肌膚如雪降宅。 梳的紋絲不亂的頭發(fā)上骂远,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音腰根,去河邊找鬼激才。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的瘸恼。 我是一名探鬼主播劣挫,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼东帅!你這毒婦竟也來了压固?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤靠闭,失蹤者是張志新(化名)和其女友劉穎帐我,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體愧膀,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡拦键,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了檩淋。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片矿咕。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖狼钮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情捡絮,我是刑警寧澤熬芜,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站福稳,受9級特大地震影響涎拉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜的圆,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一鼓拧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧越妈,春花似錦季俩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至阎抒,卻和暖如春酪我,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背且叁。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工都哭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓欺矫,卻偏偏與公主長得像纱新,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子汇陆,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

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