成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Python增強的生成器:協程

開發 后端
本文主要介紹python中Enhanced generator即coroutine相關內容,包括基本語法、使用場景、注意事項,以及與其他語言協程實現的異同。

[[202482]]

本文主要介紹python中Enhanced generator即coroutine相關內容,包括基本語法、使用場景、注意事項,以及與其他語言協程實現的異同。

enhanced generator

在上文《Python Yield Generator 詳解》中介紹了yield和generator的使用場景和主意事項,只用到了generator的next方法,事實上generator還有更強大的功能。PEP 342為generator增加了一系列方法來使得generator更像一個協程Coroutine。做主要的變化在于早期的yield只能返回值(作為數據的產生者), 而新增加的send方法能在generator恢復的時候消費一個數值,而去caller(generator的調用著)也可以通過throw在generator掛起的主動拋出異常。

  1. back_data = yield cur_ret 

這段代碼的意思是:當執行到這條語句時,返回cur_ret給調用者;并且當generator通過next()或者send(some_data)方法恢復的時候,將some_data賦值給back_data.例如:

  1. def gen(data): 
  2.  
  3.     print 'before yield', data 
  4.  
  5.     back_data = yield data 
  6.  
  7.     print 'after resume', back_data 
  8.  
  9.      
  10.  
  11. if __name__ == '__main__'
  12.  
  13.     g = gen(1) 
  14.  
  15.     print g.next() 
  16.  
  17.     try: 
  18.  
  19.         g.send(0) 
  20.  
  21.     except StopIteration: 
  22.  
  23.         pass  

輸出:

  1. before yield 1 
  2.  
  3.  
  4. after resume 0  

兩點需要注意:

  1. next() 等價于 send(None)
  2. ***次調用時,需要使用next()語句或是send(None),不能使用send發送一個非None的值,否則會出錯的,因為沒有Python yield語句來接收這個值。

應用場景

當generator可以接受數據(在從掛起狀態恢復的時候) 而不僅僅是返回數據時, generator就有了消費數據(push)的能力。下面的例子來自這里:

  1. word_map = {} 
  2.  
  3. def consume_data_from_file(file_name, consumer): 
  4.  
  5.     for line in file(file_name): 
  6.  
  7.         consumer.send(line)      
  8.  
  9. def consume_words(consumer): 
  10.  
  11.     while True
  12.  
  13.         line = yield 
  14.  
  15.         for word in (w for w in line.split() if w.strip()): 
  16.  
  17.             consumer.send(word)      
  18.  
  19. def count_words_consumer(): 
  20.  
  21.     while True
  22.  
  23.         word  = yield 
  24.  
  25.         if word not in word_map: 
  26.  
  27.             word_map[word] = 0 
  28.  
  29.         word_map[word] += 1 
  30.  
  31.     print word_map      
  32.  
  33. if __name__ == '__main__'
  34.  
  35.     cons = count_words_consumer() 
  36.  
  37.     cons.next() 
  38.  
  39.     cons_inner = consume_words(cons) 
  40.  
  41.     cons_inner.next() 
  42.  
  43.     c = consume_data_from_file('test.txt', cons_inner) 
  44.  
  45.     print word_map  

上面的代碼中,真正的數據消費者是count_words_consumer,最原始的數據生產者是consume_data_from_file,數據的流向是主動從生產者推向消費者。不過上面第22、24行分別調用了兩次next,這個可以使用一個decorator封裝一下。

  1. def consumer(func): 
  2.  
  3.     def wrapper(*args,**kw): 
  4.  
  5.         gen = func(*args, **kw) 
  6.  
  7.         gen.next() 
  8.  
  9.         return gen 
  10.  
  11.     wrapper.__name__ = func.__name__ 
  12.  
  13.     wrapper.__dict__ = func.__dict__ 
  14.  
  15.     wrapper.__doc__  = func.__doc__ 
  16.  
  17.     return wrapper  

修改后的代碼:

  1. def consumer(func): 
  2.  
  3.     def wrapper(*args,**kw): 
  4.  
  5.         gen = func(*args, **kw) 
  6.  
  7.         gen.next() 
  8.  
  9.         return gen 
  10.  
  11.     wrapper.__name__ = func.__name__ 
  12.  
  13.     wrapper.__dict__ = func.__dict__ 
  14.  
  15.     wrapper.__doc__  = func.__doc__ 
  16.  
  17.     return wrapper      
  18.  
  19. word_map = {} 
  20.  
  21. def consume_data_from_file(file_name, consumer): 
  22.  
  23.     for line in file(file_name): 
  24.  
  25.         consumer.send(line)      
  26.  
  27. @consumer 
  28.  
  29. def consume_words(consumer): 
  30.  
  31.     while True
  32.  
  33.         line = yield 
  34.  
  35.         for word in (w for w in line.split() if w.strip()): 
  36.  
  37.             consumer.send(word)      
  38.  
  39. @consumer 
  40.  
  41. def count_words_consumer(): 
  42.  
  43.     while True
  44.  
  45.         word  = yield 
  46.  
  47.         if word not in word_map: 
  48.  
  49.             word_map[word] = 0 
  50.  
  51.         word_map[word] += 1 
  52.  
  53.     print word_map      
  54.  
  55. if __name__ == '__main__'
  56.  
  57.     cons = count_words_consumer() 
  58.  
  59.     cons_inner = consume_words(cons) 
  60.  
  61.     c = consume_data_from_file('test.txt', cons_inner) 
  62.  
  63.     print word_map      
  64.  
  65. example_with_deco  

generator throw

除了next和send方法,generator還提供了兩個實用的方法,throw和close,這兩個方法加強了caller對generator的控制。send方法可以傳遞一個值給generator,throw方法在generator掛起的地方拋出異常,close方法讓generator正常結束(之后就不能再調用next send了)。下面詳細介紹一下throw方法。

  1. throw(type[, value[, traceback]]) 

在generator yield的地方拋出type類型的異常,并且返回下一個被yield的值。如果type類型的異常沒有被捕獲,那么會被傳給caller。另外,如果generator不能yield新的值,那么向caller拋出StopIteration異常:

  1. @consumer 
  2.  
  3. def gen_throw(): 
  4.  
  5.     value = yield 
  6.  
  7.     try: 
  8.         yield value 
  9.  
  10.     except Exception, e: 
  11.  
  12.         yield str(e) # 如果注釋掉這行,那么會拋出StopIteration      
  13.  
  14. if __name__ == '__main__'
  15.  
  16.     g = gen_throw() 
  17.  
  18.     assert g.send(5) == 5 
  19.  
  20.     assert g.throw(Exception, 'throw Exception') == 'throw Exception'  

***次調用send,代碼返回value(5)之后在第5行掛起, 然后generator throw之后會被第6行catch住。如果第7行沒有重新yield,那么會重新拋出StopIteration異常。

注意事項

如果一個生成器已經通過send開始執行,那么在其再次yield之前,是不能從其他生成器再次調度到該生成器

  1. @consumer 
  2.  
  3. def funcA(): 
  4.  
  5.     while True
  6.  
  7.         data = yield 
  8.  
  9.         print 'funcA recevie', data 
  10.  
  11.         fb.send(data * 2)      
  12.  
  13. @consumer 
  14.  
  15. def funcB(): 
  16.  
  17.     while True
  18.  
  19.         data = yield 
  20.  
  21.         print 'funcB recevie', data 
  22.  
  23.         fa.send(data * 2)      
  24.  
  25. fa = funcA() 
  26.  
  27. fb = funcB() 
  28.  
  29. if __name__ == '__main__'
  30.  
  31.     fa.send(10)  

輸出:

  1. funcA recevie 10 
  2.  
  3. funcB recevie 20 
  4.  
  5. ValueError: generator already executing  

Generator 與 Coroutine

回到Coroutine,可參見維基百科解釋,而我自己的理解比較簡單(或者片面):程序員可控制的并發流程,不管是進程還是線程,其切換都是操作系統在調度,而對于協程,程序員可以控制什么時候切換出去,什么時候切換回來。協程比進程 線程輕量級很多,較少了上下文切換的開銷。另外,由于是程序員控制調度,一定程度上也能避免一個任務被中途中斷.。協程可以用在哪些場景呢,我覺得可以歸納為非阻塞等待的場景,如游戲編程,異步IO,事件驅動。

Python中,generator的send和throw方法使得generator很像一個協程(coroutine), 但是generator只是一個半協程(semicoroutines),python doc是這樣描述的:

“All of this makes generator functions quite similar to coroutines; they yield multiple times, they have more than one entry point and their execution can be suspended. The only difference is that a generator function cannot control where should the execution continue after it yields; the control is always transferred to the generator’s caller.”

盡管如此,利用enhanced generator也能實現更強大的功能。比如上文中提到的yield_dec的例子,只能被動的等待時間到達之后繼續執行。在某些情況下比如觸發了某個事件,我們希望立即恢復執行流程,而且我們也關心具體是什么事件,這個時候就需要在generator send了。另外一種情形,我們需要終止這個執行流程,那么刻意調用close,同時在代碼里面做一些處理,偽代碼如下:

  1. @yield_dec 
  2.  
  3. def do(a): 
  4.  
  5.     print 'do', a 
  6.  
  7.     try: 
  8.  
  9.         event = yield 5 
  10.  
  11.         print 'post_do', a, event 
  12.  
  13.     finally: 
  14.  
  15.         print 'do sth'  

至于之前提到的另一個例子,服務(進程)之間的異步調用,也是非常適合實用協程的例子。callback的方式會割裂代碼,把一段邏輯分散到多個函數,協程的方式會好很多,至少對于代碼閱讀而言。其他語言,比如C#、Go語言,協程都是標準實現,特別對于go語言,協程是高并發的基石。在python3.x中,通過asyncio和async\await也增加了對協程的支持。在筆者所使用的2.7環境下,也可以使用greenlet,之后會有博文介紹。

參考

  • https://www.python.org/dev/peps/pep-0342/
  • http://www.dabeaz.com/coroutines/
  • https://en.wikipedia.org/wiki/Coroutine#Implementations_for_Python 
責任編輯:龐桂玉 來源: Python開發者
相關推薦

2021-09-16 09:59:13

PythonJavaScript代碼

2021-12-04 22:07:44

Python

2024-02-05 09:06:25

Python協程Asyncio庫

2023-08-25 09:51:21

前端開發

2015-08-25 11:07:58

2017-07-01 16:02:39

分布式ID生成器

2025-01-23 08:36:27

CSS開發工具

2021-08-11 20:20:51

Python異步列表

2011-12-23 13:42:05

JavaScript

2010-09-07 16:31:17

SQL語句insert

2021-04-22 21:15:38

Generator函數生成器

2022-07-25 10:27:36

背景生成器工具前端

2017-06-26 16:26:15

Python迭代對象迭代器

2021-12-09 06:41:56

Python協程多并發

2020-10-05 21:57:43

Python生成器可迭代對象

2022-10-17 18:29:55

2023-05-04 16:24:10

人工智能圖像生成器

2023-02-07 16:11:41

2023-11-15 13:35:00

迭代器生成器Python

2017-09-22 16:08:16

Python協程編程
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 91精品国产欧美一区二区成人 | 91久久夜色精品国产网站 | 国产偷录视频叫床高潮对白 | 天天综合网天天综合色 | 亚洲欧美一区二区三区在线 | 日韩国产欧美一区 | 99re视频在线观看 | 国产日韩欧美电影 | 三级在线观看 | 亚洲综合大片69999 | 国产精品毛片久久久久久 | 国产日韩一区二区三区 | 国产在线视频一区二区董小宛性色 | 国产精品久久久久久妇女 | 久草新在线 | 欧美精品欧美精品系列 | 国产视频福利一区 | 91久久| 国产精品一区在线播放 | 中文成人在线 | 欧美精品一区二区免费视频 | 欧美精品一区二区三区在线 | 国产福利视频导航 | 成人欧美一区二区三区在线播放 | 黄色一级大片在线免费看产 | 久久久这里只有17精品 | 一区二区三区四区日韩 | 日韩精品一二三 | 国产精品一区三区 | 午夜色播 | 色在线免费 | 91视视频在线观看入口直接观看 | 羞羞午夜 | 欧美成人综合 | 久亚州在线播放 | 国产欧美在线一区 | 91久久夜色 | 91精品国产91久久综合桃花 | 国产极品粉嫩美女呻吟在线看人 | 国产乱码久久久久久一区二区 | 久久精品美女 |