99热99这里只有精品6国产,亚洲中文字幕在线天天更新,在线观看亚洲精品国产福利片 ,久久久久综合网

歡迎加入QQ討論群258996829
麥子學(xué)院 頭像
蘋果6袋
6
麥子學(xué)院

python協(xié)程:yield的使用

發(fā)布時間:2017-06-14 16:32  回復(fù):0  查看:2944   最后回復(fù):2017-06-14 16:32  

本文和大家分享的主要是python協(xié)程yield相關(guān)內(nèi)容,一起來看看吧,希望對大家學(xué)習(xí)python有所幫助。

  協(xié)程定義

  協(xié)程的底層架構(gòu)是在pep342 中定義,并在python2.5 實(shí)現(xiàn)的。

  python2.5 中,yield關(guān)鍵字可以在表達(dá)式中使用,而且生成器API中增加了 .send(value)方法。生成器可以使用.send(...)方法發(fā)送數(shù)據(jù),發(fā)送的數(shù)據(jù)會成為生成器函數(shù)中yield表達(dá)式的值。

  協(xié)程是指一個過程,這個過程與調(diào)用方協(xié)作,產(chǎn)出有調(diào)用方提供的值。因此,生成器可以作為協(xié)程使用。

  除了 .send(...)方法,pep342 和添加了 .throw(...)(讓調(diào)用方拋出異常,在生成器中處理)和.close()(終止生成器)方法。

  python3.3后,pep380對生成器函數(shù)做了兩處改動:

  · 生成器可以返回一個值;以前,如果生成器中給return語句提供值,會拋出SyntaxError異常。

  · 引入yield from 語法,使用它可以把復(fù)雜的生成器重構(gòu)成小型的嵌套生成器,省去之前把生成器的工作委托給子生成器所需的大量模板代碼。

  協(xié)程生成器的基本行為

  首先說明一下,協(xié)程有四個狀態(tài),可以使用inspect.getgeneratorstate(...)函數(shù)確定:

  · GEN_CREATED # 等待開始執(zhí)行

  · GEN_RUNNING # 解釋器正在執(zhí)行(只有在多線程應(yīng)用中才能看到這個狀態(tài))

  · GEN_SUSPENDED # yield表達(dá)式處暫停

  · GEN_CLOSED # 執(zhí)行結(jié)束

  #! -*- coding: utf-8 -*-import inspect

  # 協(xié)程使用生成器函數(shù)定義:定義體中有yield關(guān)鍵字。def simple_coroutine():

  print('-> coroutine started')

  # yield 在表達(dá)式中使用;如果協(xié)程只需要從客戶那里接收數(shù)據(jù),yield關(guān)鍵字右邊不需要加表達(dá)式(yield默認(rèn)返回None

  x = yield

  print('-> coroutine received:', x)

  my_coro = simple_coroutine()

  my_coro # 和創(chuàng)建生成器的方式一樣,調(diào)用函數(shù)得到生成器對象。# 協(xié)程處于 GEN_CREATED (等待開始狀態(tài))

  print(inspect.getgeneratorstate(my_coro))

  my_coro.send(None)# 首先要調(diào)用next()函數(shù),因?yàn)樯善鬟€沒有啟動,沒有在yield語句處暫停,所以開始無法發(fā)送數(shù)據(jù)# 發(fā)送 None 可以達(dá)到相同的效果 my_coro.send(None)

  next(my_coro)# 此時協(xié)程處于 GEN_SUSPENDED (yield表達(dá)式處暫停)

  print(inspect.getgeneratorstate(my_coro))

  # 調(diào)用這個方法后,協(xié)程定義體中的yield表達(dá)式會計(jì)算出42;現(xiàn)在協(xié)程會恢復(fù),一直運(yùn)行到下一個yield表達(dá)式,或者終止。

  my_coro.send(42)

  print(inspect.getgeneratorstate(my_coro))

  運(yùn)行上述代碼,輸出結(jié)果如下

  GEN_CREATED

  -> coroutine startedGEN_SUSPENDED

  -> coroutine received: 42

  # 這里,控制權(quán)流動到協(xié)程定義體的尾部,導(dǎo)致生成器像往常一樣拋出StopIteration異常Traceback (most recent call last):

  File "/Users/gs/coroutine.py", line 18, in <module>

  my_coro.send(42)

  StopIteration

  send方法的參數(shù)會成為暫停yield表達(dá)式的值,所以,僅當(dāng)協(xié)程處于暫停狀態(tài)是才能調(diào)用send方法。

  如果協(xié)程還未激活(GEN_CREATED 狀態(tài))要調(diào)用next(my_coro) 激活協(xié)程,也可以調(diào)用my_coro.send(None)

  如果創(chuàng)建協(xié)程對象后立即把None之外的值發(fā)給它,會出現(xiàn)下述錯誤:

  >>> my_coro = simple_coroutine()>>> my_coro.send(123)

  Traceback (most recent call last):

  File "/Users/gs/coroutine.py", line 14, in <module>

  my_coro.send(123)TypeError: can't send non-None value to a just-started generator

  仔細(xì)看錯誤消息

  can't send non-None value to a just-started generator

  最先調(diào)用next(my_coro) 這一步通常稱為預(yù)激prime)協(xié)程---即,讓協(xié)程向前執(zhí)行到第一個yield表達(dá)式,準(zhǔn)備好作為活躍的協(xié)程使用。

  再看一個兩個值得協(xié)程

  def simple_coro2(a):

  print('-> coroutine started: a =', a)

  b = yield a

  print('-> Received: b =', b)

  c = yield a + b

  print('-> Received: c =', c)

  my_coro2 = simple_coro2(14)

  print(inspect.getgeneratorstate(my_coro2))# 這里inspect.getgeneratorstate(my_coro2) 得到結(jié)果為 GEN_CREATED (協(xié)程未啟動)

  next(my_coro2)# 向前執(zhí)行到第一個yield 處 打印 “-> coroutine started: a = 14”# 并且產(chǎn)生值 14 yield a 執(zhí)行 等待為b賦值)

  print(inspect.getgeneratorstate(my_coro2))# 這里inspect.getgeneratorstate(my_coro2) 得到結(jié)果為 GEN_SUSPENDED (協(xié)程處于暫停狀態(tài))

  my_coro2.send(28)# 向前執(zhí)行到第二個yield 處 打印 “-> Received: b = 28”# 并且產(chǎn)生值 a + b = 42yield a + b 執(zhí)行 得到結(jié)果42 等待為c賦值)

  print(inspect.getgeneratorstate(my_coro2))# 這里inspect.getgeneratorstate(my_coro2) 得到結(jié)果為 GEN_SUSPENDED (協(xié)程處于暫停狀態(tài))

  my_coro2.send(99)# 把數(shù)字99發(fā)送給暫停協(xié)程,計(jì)算yield 表達(dá)式,得到99,然后把那個數(shù)賦值給c 打印 “-> Received: c = 99”# 協(xié)程終止,拋出StopIteration

  運(yùn)行上述代碼,輸出結(jié)果如下

  GEN_CREATED

  -> coroutine started: a = 14GEN_SUSPENDED

  -> Received: b = 28

  -> Received: c = 99

  Traceback (most recent call last):

  File "/Users/gs/coroutine.py", line 37, in <module>

  my_coro2.send(99)

  StopIteration

  simple_coro2 協(xié)程的執(zhí)行過程分為3個階段,如下圖所示

python協(xié)程:yield的使用 

  1. 調(diào)用next(my_coro2),打印第一個消息,然后執(zhí)行yield a,產(chǎn)出數(shù)字14.

  2. 調(diào)用my_coro2.send(28),把28賦值給b,打印第二個消息,然后執(zhí)行 yield a + b 產(chǎn)生數(shù)字42

  3. 調(diào)用my_coro2.send(99),把99賦值給c,然后打印第三個消息,協(xié)程終止。

  使用裝飾器預(yù)激協(xié)程

  我們已經(jīng)知道,協(xié)程如果不預(yù)激,不能使用send() 傳入非None 數(shù)據(jù)。所以,調(diào)用my_coro.send(x)之前,一定要調(diào)用next(my_coro)。

  為了簡化,我們會使用裝飾器預(yù)激協(xié)程。

  from functools import wraps

  def coroutinue(func):

  '''

  裝飾器: 向前執(zhí)行到第一個`yield`表達(dá)式,預(yù)激`func`

  :param func: func name

  :return: primer

  '''

  @wraps(func)

  def primer(*args, **kwargs):

  # 把裝飾器生成器函數(shù)替換成這里的primer函數(shù);調(diào)用primer函數(shù)時,返回預(yù)激后的生成器。

  gen = func(*args, **kwargs)

  # 調(diào)用被被裝飾函數(shù),獲取生成器對象

  next(gen)  # 預(yù)激生成器

  return gen  # 返回生成器

  return primer

  # 使用方法如下

  @coroutinuedef simple_coro(a):

  a = yield

  simple_coro(12)  # 已經(jīng)預(yù)激

  終止協(xié)程和異常處理

  協(xié)程中,為處理的異常會向上冒泡,傳遞給next函數(shù)或send方法的調(diào)用方,未處理的異常會導(dǎo)致協(xié)程終止。

  看下邊這個例子

  #! -*- coding: utf-8 -*-

  from functools import wraps

  def coroutinue(func):

  '''

  裝飾器: 向前執(zhí)行到第一個`yield`表達(dá)式,預(yù)激`func`

  :param func: func name

  :return: primer

  '''

  @wraps(func)

  def primer(*args, **kwargs):

  # 把裝飾器生成器函數(shù)替換成這里的primer函數(shù);調(diào)用primer函數(shù)時,返回預(yù)激后的生成器。

  gen = func(*args, **kwargs)

  # 調(diào)用被被裝飾函數(shù),獲取生成器對象

  next(gen)  # 預(yù)激生成器

  return gen  # 返回生成器

  return primer

  @coroutinuedef averager():

  # 使用協(xié)程求平均值

  total = 0.0

  count = 0

  average = None

  while True:

  term = yield average

  total += term

  count += 1

  average = total/count

  coro_avg = averager()

  print(coro_avg.send(40))

  print(coro_avg.send(50))

  print(coro_avg.send('123')) # 由于發(fā)送的不是數(shù)字,導(dǎo)致內(nèi)部有異常拋出。

  執(zhí)行上述代碼結(jié)果如下

  40.0

  45.0

  Traceback (most recent call last):

  File "/Users/gs/coro_exception.py", line 37, in <module>

  print(coro_avg.send('123'))

  File "/Users/gs/coro_exception.py", line 30, in averager

  total += term

  TypeError: unsupported operand type(s) for +=: 'float' and 'str'

  出錯的原因是發(fā)送給協(xié)程的'123'值不能加到total變量上。

  出錯后,如果再次調(diào)用 coro_avg.send(x) 方法 會拋出 StopIteration 異常。

  由上邊的例子我們可以知道,如果想讓協(xié)程退出,可以發(fā)送給它一個特定的值。比如NoneEllipsis。(推薦使用Ellipsis,因?yàn)槲覀儾惶褂眠@個值)

  從Python2.5 開始,我們可以在生成器上調(diào)用兩個方法,顯式的把異常發(fā)給協(xié)程。

  這兩個方法是throwclose

  generator.throw(exc_type[, exc_value[, traceback]])

  這個方法使生成器在暫停的yield表達(dá)式處拋出指定的異常。如果生成器處理了拋出的異常,代碼會向前執(zhí)行到下一個yield表達(dá)式,而產(chǎn)出的值會成為調(diào)用throw方法得到的返回值。如果沒有處理,則向上冒泡,直接拋出。

  generator.close()

  生成器在暫停的yield表達(dá)式處拋出GeneratorExit異常。

  如果生成器沒有處理這個異?;蛘邟伋隽?/span>StopIteration異常,調(diào)用方不會報(bào)錯。如果收到GeneratorExit異常,生成器一定不能產(chǎn)出值,否則解釋器會拋出RuntimeError異常。

  示例: 使用closethrow方法控制協(xié)程。

  import inspect

  class DemoException(Exception):

  pass

  @coroutinuedef exc_handling():

  print('-> coroutine started')

  while True:

  try:

  x = yield

  except DemoException:

  print('*** DemoException handled. Conginuing...')

  else:

  # 如果沒有異常顯示接收到的值

  print('--> coroutine received: {!r}'.format(x))

  raise RuntimeError('This line should never run.')  # 這一行永遠(yuǎn)不會執(zhí)行

  exc_coro = exc_handling()

  exc_coro.send(11)

  exc_coro.send(12)

  exc_coro.send(13)

  exc_coro.close()

  print(inspect.getgeneratorstate(exc_coro))

  raise RuntimeError('This line should never run.') 永遠(yuǎn)不會執(zhí)行,因?yàn)橹挥形刺幚淼漠惓2艜K止循環(huán),而一旦出現(xiàn)未處理的異常,協(xié)程會立即終止。

  執(zhí)行上述代碼得到結(jié)果為:

  -> coroutine started

  --> coroutine received: 11

  --> coroutine received: 12

  --> coroutine received: 13

  GEN_CLOSED    # 協(xié)程終止

  上述代碼,如果傳入DemoException,協(xié)程不會中止,因?yàn)樽隽水惓L幚怼?/span>

  exc_coro = exc_handling()

  exc_coro.send(11)

  exc_coro.send(12)

  exc_coro.send(13)

  exc_coro.throw(DemoException) # 協(xié)程不會中止,但是如果傳入的是未處理的異常,協(xié)程會終止print(inspect.getgeneratorstate(exc_coro))

  exc_coro.close()print(inspect.getgeneratorstate(exc_coro))

  ## output

  -> coroutine started

  --> coroutine received: 11

  --> coroutine received: 12

  --> coroutine received: 13

  *** DemoException handled. Conginuing...

  GEN_SUSPENDED

  GEN_CLOSED

  如果不管協(xié)程如何結(jié)束都想做些處理工作,要把協(xié)程定義體重的相關(guān)代碼放入try/finally塊中。

  @coroutinuedef exc_handling():

  print('-> coroutine started')

  try:

  while True:

  try:

  x = yield

  except DemoException:

  print('*** DemoException handled. Conginuing...')

  else:

  # 如果沒有異常顯示接收到的值

  print('--> coroutine received: {!r}'.format(x))

  finally:

  print('-> coroutine ending')



來源:簡書

您還未登錄,請先登錄

熱門帖子

最新帖子

?