學Python,不明白協(xié)程那就虧大了

學習Python很多書籍都或多或少介紹協(xié)程,但是很少有人會在開發(fā)中使用握巢,一是理解不深晕鹊,使用怕出錯,二是有其他方式可以替代暴浦,知識代碼多一點溅话,便于理解啊,總之:不使用的理由千千種歌焦。
今天我就來和大家聊聊Python協(xié)程

  1. 協(xié)程是啥

  2. 協(xié)程在實際的應用

協(xié)程的定義

協(xié)程 是為非搶占式多任務產(chǎn)生子程序的計算機程序組件公荧,協(xié)程允許不同入口點在不同位置暫停或開始執(zhí)行程序”同规。從技術的角度來說循狰,“協(xié)程就是你可以暫停執(zhí)行的函數(shù)”

與生成器類似窟社,但yield 出現(xiàn)在表達式的右邊,且產(chǎn)出一個表達式值作為返回值绪钥,如果yield后沒有表達式灿里,則返回None。協(xié)程可以從調(diào)用方接收數(shù)據(jù)程腹,caller.send(params)匣吊。

1. 例子
>>> def simple(a):
    print "start:a = ", (a)
    b = yield a
    print "received b= ",(b)
    c = yield a + b
    print "received :d ", (c)

    
>>> m1 = simple(2)
>>> m1
<generator object simple at 0x0000000003D830D8>
>>> next(m1) # 激活協(xié)程, 并產(chǎn)出a,然后暫停
start:a =  2
2
>>> m1.send(3)
received b=  3
5
>>> m1.send(1000) # b 接收數(shù)據(jù)寸潦,并產(chǎn)出 a+b 的結(jié)果色鸳,然后暫停 
received :d  1000

Traceback (most recent call last):
  File "<pyshell#11>", line 1, in <module>
    m1.send(1000)
StopIteration

yield作為控制流程的方式來理解。

image.png

上圖中 第一階段 yield a結(jié)束见转,第二階段是給b 賦值開始

再看一個例子:

def averager():
    total = 0
    count = 0
    avg = None
    while True:
        term = yield avg 
        total += term
        count +=1
        avg = total/count

ag = averager()
next(ag) # 運行到 yield avg 就停止命雀,并返回avg值 None
ag.send(100) # item 接收到100,并運行,直到下一個yield 并停止

2. 與協(xié)程相關的方法

我們知道了怎么創(chuàng)建協(xié)程斩箫,但是當執(zhí)行到最后吏砂,如果不處理就會和生成器一樣拋出一個stopInteration異常來終止該協(xié)程,從2.5版本開始添加了兩個方法throwclose來終止協(xié)程

如果generator.thow(exception)的異常被捕獲乘客,則繼續(xù)下一個yield,否則終止

3. 協(xié)程返回值

在定義體中 return value

from collections import namedTuple
Result = namedTuple("Result","count average")

def average():
      total = 0
      count = 0
      avg = None
      while True:
             term = yield
             if term is None:
                break   # 終止符
             total +=term
             count +=1
             avg = total/count
     return Result(count,avg)

send(None) 就會break, 返回狐血。其中Result 會作為異常的value屬性返回

try:
     m2 .send(None)
exception StopInteration as exc:
     print exc.value

應用



# BEGIN YIELD_FROM_AVERAGER
from collections import namedtuple

Result = namedtuple('Result', 'count average')


# the subgenerator
def averager():  # <1>
    total = 0.0
    count = 0
    average = None
    while True:
        term = yield  # <2>
        if term is None:  # <3>
            break
        total += term
        count += 1
        average = total/count
    return Result(count, average)  # <4>


# the delegating generator
def grouper(results, key):  # <5>
    while True:  # <6>
        results[key] = yield from averager()  #生成一個使用協(xié)程的生成器,在此處暫停易核,等 將一個Result 賦值給對應的key


# the client code, a.k.a. the caller
def main(data):  # <8>
    results = {}
    for key, values in data.items():
        group = grouper(results, key)  # group是grouper函數(shù)生成的生成器對象
        next(group)  # 預激活協(xié)程
        for value in values:
# 把各個 value 傳給 grouper匈织。傳入的值最終到達 averager 函數(shù)中 term = yield 那一行; grouper 永遠不知道傳入的值是什么
            group.send(value)  

# 內(nèi)層循環(huán)結(jié)束后牡直, group 實例依舊在 yield from 表達式處暫停报亩,因此, grouper函數(shù)定義體中為 results[key] 賦值的語句還沒有執(zhí)行

#把 None 傳入 grouper井氢,導致當前的 averager 實例終止,也讓 grouper 繼續(xù)運行岳链,再創(chuàng)建一個 averager 實例花竞,處理下一組值
        group.send(None) 

    # print(results)  # uncomment to debug
    report(results)


# output report
def report(results):
    for key, result in sorted(results.items()):
        group, unit = key.split(';')
        print('{:2} {:5} averaging {:.2f}{}'.format(
              result.count, group, result.average, unit))


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],
}


if __name__ == '__main__':
    main(data)

上述例子,如果子生成器不停止掸哑,委派生成器永遠在yield from 處停止约急。

yield from iterable本質(zhì)上等于for item in iterable: yield item的縮寫

yield from的意義

把迭代器當作生成器使用,相當于把子生成器的定義體內(nèi)聯(lián)在 yield from 表達式
中苗分。此外厌蔽,子生成器可以執(zhí)行 return 語句,返回一個值摔癣,而返回的值會成為 yield
from 表達式的值

  1. 子生成器產(chǎn)出的值都直接傳給委派生成器的調(diào)用方(即客戶端代碼將上述例子的averager 直接給了main中的group)奴饮。

  2. 使用 send() 方法發(fā)給委派生成器的值都直接傳給子生成器纬向。如果發(fā)送的值是
    None,那么會調(diào)用子生成器的 __next__()方法戴卜。如果發(fā)送的值不是None逾条,那么會
    調(diào)用子生成器的 send()方法。如果調(diào)用的方法拋出 StopIteration 異常投剥,那么委
    派生成器恢復運行师脂。任何其他異常都會向上冒泡,傳給委派生成器江锨。

  3. 生成器退出時吃警,生成器(或子生成器)中的return expr表達式會觸發(fā)
    StopIteration(expr)異常拋出。

  4. yield from 表達式的值是子生成器終止時傳給 StopIteration 異常的第一個參
    數(shù)啄育。

** yield from 結(jié)構的另外兩個特性與異常和終止有關酌心。**

  • 傳入委派生成器的異常,除了GeneratorExit之外都傳給子生成器的throw()
    法灸撰。如果調(diào)用 throw()方法時拋出StopIteration 異常谒府,委派生成器恢復運
    行。 StopIteration 之外的異常會向上冒泡浮毯,傳給委派生成器完疫。
  • 如果把GeneratorExit 異常傳入委派生成器,或者在委派生成器上調(diào)用close()
    法债蓝,那么在子生成器上調(diào)用close()方法壳鹤,如果它有的話。如果調(diào)用close() 方法
    導致異常拋出饰迹,那么異常會向上冒泡芳誓,傳給委派生成器;否則啊鸭,委派生成器拋出
    GeneratorExit異常锹淌。
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市赠制,隨后出現(xiàn)的幾起案子赂摆,更是在濱河造成了極大的恐慌,老刑警劉巖钟些,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件烟号,死亡現(xiàn)場離奇詭異,居然都是意外死亡政恍,警方通過查閱死者的電腦和手機汪拥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來篙耗,“玉大人迫筑,你說我怎么就攤上這事宪赶。” “怎么了铣焊?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵逊朽,是天一觀的道長。 經(jīng)常有香客問我曲伊,道長叽讳,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任坟募,我火速辦了婚禮岛蚤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘懈糯。我一直安慰自己涤妒,他們只是感情好,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布赚哗。 她就那樣靜靜地躺著拒炎,像睡著了一般晌姚。 火紅的嫁衣襯著肌膚如雪皂冰。 梳的紋絲不亂的頭發(fā)上吠裆,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天,我揣著相機與錄音够掠,去河邊找鬼民褂。 笑死,一個胖子當著我的面吹牛疯潭,可吹牛的內(nèi)容都是我干的赊堪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼竖哩,長吁一口氣:“原來是場噩夢啊……” “哼哭廉!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起相叁,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤遵绰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后钝荡,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡舶衬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年埠通,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片逛犹。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡端辱,死狀恐怖梁剔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情舞蔽,我是刑警寧澤荣病,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站渗柿,受9級特大地震影響个盆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜朵栖,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一颊亮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧陨溅,春花似錦终惑、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至臼寄,卻和暖如春霸奕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背脯厨。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工铅祸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人合武。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓临梗,卻偏偏與公主長得像,于是被迫代替她去往敵國和親稼跳。 傳聞我的和親對象是個殘疾皇子盟庞,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354

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