Python進階編程之產生器(Generator)與coroutine(二):coroutine與pipeline(管道)和Dataflow(資料流_

來源:互聯網
上載者:User

標籤:

原創作品,轉載請註明出處:點我

 

在前兩篇文章中,我們介紹了什麼是Generator和coroutine,在這一篇文章中,我們會介紹coroutine在類比pipeline(管道 )和控制Dataflow(資料流)方面的運用。

coroutine可以用來類比pipeline行為。通過把多個coroutine串聯在一起來實現pipe,在這個管道中,資料是通過send()函數在各個coroutine之間傳遞的:

但是這些在pipe中傳遞的資料哪裡來的呢?這就需要一個資料來源,或者說producer.這個producer驅動整個pipe的運行:

通常情況下,source只是提供資料,驅動整個pipe的運行,其本身並不是一個coroutine,通常行為類似於下面這個模式:

其中,target就是一個coroutine,當調用target.send()函數的時候,資料將會傳入整個pipe。

既然pipeline有一個起點,同樣的也就必須要有一個sink(end-point,也就是終點)

sink是收集接受coroutine傳送過來的資料並對這些資料進行處理。sink通常的模式為:

在前面講述Generetor的文章中,我們用Generator實現了unix中的tail -f命令和tail -f  | grep 命令,在這裡,我們也用coroutine來實現這兩個命令。

先來看看作為source的代碼unix_tail_f_co()函數

 1 # A source that mimics Unix "tail -f" 2 def unix_tail_f_co(thefile, target): 3     ‘‘‘ 4     target是一個coroutine 5     ‘‘‘ 6     thefile.seek(0, 2)  # 跳到檔案末尾 7     while True: 8         line = thefile.readline() 9         if not line:10             time.sleep(0.1)11             continue12         # 把資料發送給coroutine進行處理13         target.send(line)

在上面的代碼中,可以看到,target是一個coroutine,函數每次讀取一行資料,讀取到之後,就調用target.send()函數,把資料發送給了target,由target接收進行下一步的處理。

現在來看看作為sink的printer_co()函數,這個sink很簡單,就是簡單地列印它收到的資料。

1 # A sink just prints the lines2 @coroutine3 def printer_co():4     while True:5         # 在這個地方掛起,等待接收資料6         line = (yield)7         print line,

其中coroutine函數裝飾器使我們在上一篇介紹coroutine的文章中定義的。從代碼中可以看到,作為sink,print_co()函數有一個死迴圈,從第6行可以看到,在這個死迴圈中,

函數會一直掛起,等到資料的到來,然後每次接收到資料後,列印輸出,然後再次掛起等待資料的到來。

現在可以把上面兩個函數結合起來實現tail -f命令:

1 f = open("access-log")2 unix_tail_f_co(f,printer_co())

代碼首先開啟一個檔案f,f作為資料來源,把f和printer_co()傳遞給unix_tail_f_co(),這就實現了一個pipeline,只不過在這個pipeline中,資料是直接發送給作為sink的printer_co()函數的,中間沒有經過其他的coroutine。

在sink和source之間,可以根據需要,添加任何的coroutine,比如資料變換(transformation)、過濾(filter)和路由(routing)等

現在,我們添加一個coroutine,grep_filter_co(pattern,target),其中,target是一個coroutine

1 @coroutine2 def grep_filter_co(pattern,target):3     while True:4         # 掛起,等待接收資料5         line = (yield)6         if pattern in line:7             # 接收到的資料如果符合要求,8             # 則發送給下一個coroutine進行處理9             target.send(line)
從代碼中可以看到,grep_filter_co()有一個死迴圈,在迴圈中掛起等待接收資料,一旦接收到了資料,如果資料中存在pattern,則把接收到的資料發送給target,讓target對資料進行下一步處理,然後再次等待接收資料並掛起。
同樣的,現在把這三個函數組合起來,實現tail -f | grep命令,組成一個新的pipeline:
f = open("access-log")unix_tail_f_co(f,grep_filter_co("python",printer_co()))

unix_tail_f_co()作為source,從f檔案處每次讀取一行資料,發送給grep_filter_co()這個coroutine,grep_filer_co()對接收到的資料進行過濾(filter):如果接收到的資料包含了"python"這個單詞,就把資料發送給printer_co()進行處理,然後source再把下一行資料發送到pipeline中進行處理。

在前面也用Generator實現了tail -f | grep 命令,現在可以把兩者做一個比較:
Generator實現的流程為:

而coroutine實現的流程為:

可以看出,Generator 在最後的的迭代過程中從pipe中擷取資料,而coroutine通過send()函數把資料發送到pipeline中去。
通過coroutine,可以把資料發送到不同的目的地,如:

下面我們來實現訊息廣播(Broadcasting)機制,首先要先定義一個函數broadcast_co(targets)
1 # 把資料發送給多個不同的coroutine2 @coroutine3 def broadcast_co(targets):4     while True:5         # 掛起,等待接收資料6         item = (yield)7         for target in targets:8             # 接收到了資料,然後分別發送給不同的coroutine9             target.send(item)

broadcats_co()函數接受一個參數targets,這個參數是一個列表(list),其中的每一個成員都是coroutine,在一個死迴圈中,函數接收到資料之後,把資料依次發送給不同的coroutine進行處理,然後會掛起等待接收資料。

f = open("access-log")unix_tail_f_co(f,            broadcast_co([grep_filter_co(‘python‘,printer_co()),                         grep_filter_c0(‘ply‘,printer_co()),                         grep_filter_co(‘swig‘,printer_co())])            )            

unix_tail_f_co每次從f讀取一行資料,發送給broadcast_co(),broadcast_co()會把接收到的資料依次發送給gerp_filter_co(),每個grep_filter_co()再會把資料發送給相應的printer_co()進行處理。

                        |---------------> grep_filter_co("python") ------> printer_co() unix_tail_f_co()--->broadcast_co() ----> grep_filter_co("ply") ---------> printer_co()                        |---------------> grep_filter_co("swig")---------> printer_co()

需要注意的是:broadcast_co()會先把資料發送給grep_filter_co("python"),grep_filter_co("python")會把資料發送給printer_co(),當printer_co()執行後掛起再次等待接受資料時,執行權返回到grep_filter_co("python")函數,此時grep_filter_co("python")也會掛起等待接收資料,執行權回到broadcast_co()函數,此時broadcast_co()才會把訊息發送給grep_filter_co("ply"),也只有當grep_filter_co("ply")執行完畢掛起之後,broadcast_co()才會接著把資料發送給下一個coroutine。

如果把上面的代碼改成這樣,就會有另外一種broadcast的模式:

f = open("access-log")p = printer_co()unix_tail_f_co(f,              broadcast_co([grep_filter_co(‘python‘,p)),                            grep_filter_co(‘ply‘,p),                            grep_filter_co(‘swig‘,p)])              )

此時,broadcast的模式為

                       |---------------> grep_filter_co("python") ---------->|unix_tail_f_co()--->broadcast_co() ----> grep_filter_co("ply") ---------> printer_co()                       |---------------> grep_filter_co("swig")------------->|

最後資料都會傳送到同一個print_co()函數,也就是說最後資料的目的地為同一個。

 

好了,這篇講解coroutine在類比pipeline和控制dataflow上的應用已經完畢了,可以看出coroutine在資料路由方面有很強大的控制能力,可以把多個不同的處理方式組合在一起使用。

下一篇文章會講解如何用coroutine是下班一個簡單的多任務(Multitask)的作業系統,盡請期待O(∩_∩)O。

Python進階編程之產生器(Generator)與coroutine(二):coroutine與pipeline(管道)和Dataflow(資料流_

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.