最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解Python 協(xié)程的詳細(xì)用法使用和例子

 更新時(shí)間:2018年06月15日 10:44:07   作者:zhexiao27  
這篇文章主要介紹了詳解Python 協(xié)程的詳細(xì)用法和例子,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

從句法上看,協(xié)程與生成器類(lèi)似,都是定義體中包含 yield 關(guān)鍵字的函數(shù)??墒?,在協(xié)程中, yield 通常出現(xiàn)在表達(dá)式的右邊(例如, datum = yield),可以產(chǎn)出值,也可以不產(chǎn)出 —— 如果 yield 關(guān)鍵字后面沒(méi)有表達(dá)式,那么生成器產(chǎn)出 None。

協(xié)程可能會(huì)從調(diào)用方接收數(shù)據(jù),不過(guò)調(diào)用方把數(shù)據(jù)提供給協(xié)程使用的是 .send(datum) 方法,而不是next(…) 函數(shù)。

==yield 關(guān)鍵字甚至還可以不接收或傳出數(shù)據(jù)。不管數(shù)據(jù)如何流動(dòng), yield 都是一種流程控制工具,使用它可以實(shí)現(xiàn)協(xié)作式多任務(wù):協(xié)程可以把控制器讓步給中心調(diào)度程序,從而激活其他的協(xié)程==。

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

這里有一個(gè)最簡(jiǎn)單的協(xié)程代碼:

def simple_coroutine():
  print('-> start')
  x = yield
  print('-> recived', x)

sc = simple_coroutine()

next(sc)
sc.send('zhexiao')

解釋?zhuān)?

1. 協(xié)程使用生成器函數(shù)定義:定義體中有 yield 關(guān)鍵字。
2. yield 在表達(dá)式中使用;如果協(xié)程只需從客戶(hù)那里接收數(shù)據(jù),那么產(chǎn)出的值是 None —— 這個(gè)值是隱式指定的,因?yàn)?yield 關(guān)鍵字右邊沒(méi)有表達(dá)式。
3. 首先要調(diào)用 next(…) 函數(shù),因?yàn)樯善鬟€沒(méi)啟動(dòng),沒(méi)在 yield 語(yǔ)句處暫停,所以一開(kāi)始無(wú)法發(fā)送數(shù)據(jù)。
4. 調(diào)用send方法,把值傳給 yield 的變量,然后協(xié)程恢復(fù),繼續(xù)執(zhí)行下面的代碼,直到運(yùn)行到下一個(gè) yield 表達(dá)式,或者終止。

==注意:send方法只有當(dāng)協(xié)程處于 GEN_SUSPENDED 狀態(tài)下時(shí)才會(huì)運(yùn)作,所以我們使用 next() 方法激活協(xié)程到 yield 表達(dá)式處停止,或者我們也可以使用 sc.send(None),效果與 next(sc) 一樣==。

協(xié)程的四個(gè)狀態(tài):

協(xié)程可以身處四個(gè)狀態(tài)中的一個(gè)。當(dāng)前狀態(tài)可以使用inspect.getgeneratorstate(…) 函數(shù)確定,該函數(shù)會(huì)返回下述字符串中的一個(gè):

1. GEN_CREATED:等待開(kāi)始執(zhí)行
2. GEN_RUNNING:解釋器正在執(zhí)行
3. GEN_SUSPENED:在yield表達(dá)式處暫停
4. GEN_CLOSED:執(zhí)行結(jié)束

==最先調(diào)用 next(sc) 函數(shù)這一步通常稱(chēng)為“預(yù)激”(prime)協(xié)程==(即,讓協(xié)程向前執(zhí)行到第一個(gè) yield 表達(dá)式,準(zhǔn)備好作為活躍的協(xié)程使用)。

import inspect

def simple_coroutine(a):
  print('-> start')

  b = yield a
  print('-> recived', a, b)

  c = yield a + b
  print('-> recived', a, b, c)

# run 
sc = simple_coroutine(5)

next(sc)
sc.send(6) # 5, 6
sc.send(7) # 5, 6, 7

示例:使用協(xié)程計(jì)算移動(dòng)平均值

def averager():
  total = 0.0
  count = 0
  avg = None

  while True:
    num = yield avg
    total += num
    count += 1
    avg = total/count

# run
ag = averager()
# 預(yù)激協(xié)程
print(next(ag))   # None

print(ag.send(10)) # 10
print(ag.send(20)) # 15

解釋?zhuān)?

1. 調(diào)用 next(ag) 函數(shù)后,協(xié)程會(huì)向前執(zhí)行到 yield 表達(dá)式,產(chǎn)出 average 變量的初始值——None。
2. 此時(shí),協(xié)程在 yield 表達(dá)式處暫停。
3. 使用 send() 激活協(xié)程,把發(fā)送的值賦給 num,并計(jì)算出 avg 的值。
4. 使用 print 打印出 yield 返回的數(shù)據(jù)。

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

協(xié)程中未處理的異常會(huì)向上冒泡,傳給 next 函數(shù)或 send 方法的調(diào)用方(即觸發(fā)協(xié)程的對(duì)象)。

==終止協(xié)程的一種方式:發(fā)送某個(gè)哨符值,讓協(xié)程退出。內(nèi)置的 None 和Ellipsis 等常量經(jīng)常用作哨符值==。

顯式地把異常發(fā)給協(xié)程

從 Python 2.5 開(kāi)始,客戶(hù)代碼可以在生成器對(duì)象上調(diào)用兩個(gè)方法,顯式地把異常發(fā)給協(xié)程。

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

致使生成器在暫停的 yield 表達(dá)式處拋出指定的異常。如果生成器處理了拋出的異常,代碼會(huì)向前執(zhí)行到下一個(gè) yield 表達(dá)式,而產(chǎn)出的值會(huì)成為調(diào)用 generator.throw方法得到的返回值。如果生成器沒(méi)有處理拋出的異常,異常會(huì)向上冒泡,傳到調(diào)用方的上下文中。

generator.close()

致使生成器在暫停的 yield 表達(dá)式處拋出 GeneratorExit 異常。如果生成器沒(méi)有處理這個(gè)異常,或者拋出了 StopIteration 異常(通常是指運(yùn)行到結(jié)尾),調(diào)用方不會(huì)報(bào)錯(cuò)。如果收到 GeneratorExit 異常,生成器一定不能產(chǎn)出值,否則解釋器會(huì)拋出RuntimeError 異常。生成器拋出的其他異常會(huì)向上冒泡,傳給調(diào)用方。

異常處理示例:

class DemoException(Exception):
  """
  custom exception
  """

def handle_exception():
  print('-> start')

  while True:
    try:
      x = yield
    except DemoException:
      print('-> run demo exception')
    else:
      print('-> recived x:', x)

  raise RuntimeError('this line should never run')

he = handle_exception()
next(he)
he.send(10) # recived x: 10
he.send(20) # recived x: 20

he.throw(DemoException) # run demo exception

he.send(40) # recived x: 40
he.close()

如果傳入無(wú)法處理的異常,則協(xié)程會(huì)終止:

he.throw(Exception) # run demo exception

yield from獲取協(xié)程的返回值

為了得到返回值,協(xié)程必須正常終止;然后生成器對(duì)象會(huì)拋出StopIteration 異常,異常對(duì)象的 value 屬性保存著返回的值。

==yield from 結(jié)構(gòu)會(huì)在內(nèi)部自動(dòng)捕獲 StopIteration 異常==。對(duì) yield from 結(jié)構(gòu)來(lái)說(shuō),解釋器不僅會(huì)捕獲 StopIteration 異常,還會(huì)把value 屬性的值變成 yield from 表達(dá)式的值。

yield from基本用法

==在生成器 gen 中使用 yield from subgen() 時(shí), subgen 會(huì)獲得控制權(quán),把產(chǎn)出的值傳給 gen 的調(diào)用方,即調(diào)用方可以直接控制 subgen。與此同時(shí), gen 會(huì)阻塞,等待 subgen 終止==。

下面2個(gè)函數(shù)的作用一樣,只是使用了 yield from 的更加簡(jiǎn)潔:

def gen():
  for c in 'AB':
    yield c

print(list(gen()))

def gen_new():
  yield from 'AB'

print(list(gen_new()))

==yield from x 表達(dá)式對(duì) x 對(duì)象所做的第一件事是,調(diào)用 iter(x),從中獲取迭代器,因此, x 可以是任何可迭代的對(duì)象,這只是 yield from 最基礎(chǔ)的用法==。

yield from高級(jí)用法

==yield from 的主要功能是打開(kāi)雙向通道,把最外層的調(diào)用方與最內(nèi)層的子生成器連接起來(lái),這樣二者可以直接發(fā)送和產(chǎn)出值,還可以直接傳入異常,而不用在位于中間的協(xié)程中添加大量處理異常的樣板代碼==。

yield from 專(zhuān)門(mén)的術(shù)語(yǔ)

  1. 委派生成器:包含 yield from 表達(dá)式的生成器函數(shù)。
  2. 子生成器:從 yield from 中 部分獲取的生成器。

圖示

解釋?zhuān)?
1. 委派生成器在 yield from 表達(dá)式處暫停時(shí),調(diào)用方可以直接把數(shù)據(jù)發(fā)給子生成器。
2. 子生成器再把產(chǎn)出的值發(fā)給調(diào)用方。
3. 子生成器返回之后,解釋器會(huì)拋出 StopIteration 異常,并把返回值附加到異常對(duì)象上,此時(shí)委派生成器會(huì)恢復(fù)。

高級(jí)示例

from collections import namedtuple

ResClass = namedtuple('Res', 'count average')

# 子生成器
def averager():
  total = 0.0
  count = 0
  average = None

  while True:
    term = yield
    if term is None:
      break
    total += term
    count += 1
    average = total / count

  return ResClass(count, average)


# 委派生成器
def grouper(storages, key):
  while True:
    # 獲取averager()返回的值
    storages[key] = yield from averager()


# 客戶(hù)端代碼
def client():
  process_data = {
    'boys_2': [39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3],
    'boys_1': [1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46]
  }

  storages = {}
  for k, v in process_data.items():
    # 獲得協(xié)程
    coroutine = grouper(storages, k)

    # 預(yù)激協(xié)程
    next(coroutine)

    # 發(fā)送數(shù)據(jù)到協(xié)程
    for dt in v:
      coroutine.send(dt)

    # 終止協(xié)程
    coroutine.send(None)
  print(storages)

# run
client()

解釋?zhuān)?
1. 外層 for 循環(huán)每次迭代會(huì)新建一個(gè) grouper 實(shí)例,賦值給 coroutine 變量; grouper 是委派生成器。
2. 調(diào)用 next(coroutine),預(yù)激委派生成器 grouper,此時(shí)進(jìn)入 while True 循環(huán),調(diào)用子生成器 averager 后,在 yield from 表達(dá)式處暫停。
3. 內(nèi)層 for 循環(huán)調(diào)用 coroutine.send(value),直接把值傳給子生成器 averager。同時(shí),當(dāng)前的 grouper 實(shí)例(coroutine)在 yield from 表達(dá)式處暫停。
4. 內(nèi)層循環(huán)結(jié)束后, grouper 實(shí)例依舊在 yield from 表達(dá)式處暫停,因此, grouper函數(shù)定義體中為 results[key] 賦值的語(yǔ)句還沒(méi)有執(zhí)行。
5. coroutine.send(None) 終止 averager 子生成器,子生成器拋出 StopIteration 異常并將返回的數(shù)據(jù)包含在異常對(duì)象的value中,yield from 可以直接抓取 StopItration 異常并將異常對(duì)象的 value 賦值給 results[key]

yield from的意義

  1. 子生成器產(chǎn)出的值都直接傳給委派生成器的調(diào)用方(即客戶(hù)端代碼)。
  2. 使用 send() 方法發(fā)給委派生成器的值都直接傳給子生成器。如果發(fā)送的值是None,那么會(huì)調(diào)用子生成器的 next() 方法。如果發(fā)送的值不是 None,那么會(huì)調(diào)用子生成器的 send() 方法。如果調(diào)用的方法拋出 StopIteration 異常,那么委派生成器恢復(fù)運(yùn)行。任何其他異常都會(huì)向上冒泡,傳給委派生成器。
  3. 生成器退出時(shí),生成器(或子生成器)中的 return expr 表達(dá)式會(huì)觸發(fā) StopIteration(expr) 異常拋出。
  4. yield from 表達(dá)式的值是子生成器終止時(shí)傳給 StopIteration 異常的第一個(gè)參數(shù)。
  5. 傳入委派生成器的異常,除了 GeneratorExit 之外都傳給子生成器的 throw() 方法。如果調(diào)用 throw() 方法時(shí)拋出 StopIteration 異常,委派生成器恢復(fù)運(yùn)行。 StopIteration 之外的異常會(huì)向上冒泡,傳給委派生成器。
  6. 如果把 GeneratorExit 異常傳入委派生成器,或者在委派生成器上調(diào)用 close() 方法,那么在子生成器上調(diào)用 close() 方法,如果它有的話(huà)。如果調(diào)用close()方法導(dǎo)致異常拋出,那么異常會(huì)向上冒泡,傳給委派生成器;否則,委派生成器拋出GeneratorExit 異常。

使用案例

協(xié)程能自然地表述很多算法,例如仿真、游戲、異步 I/O,以及其他事件驅(qū)動(dòng)型編程形式或協(xié)作式多任務(wù)。協(xié)程是 asyncio 包的基礎(chǔ)構(gòu)建。通過(guò)仿真系統(tǒng)能說(shuō)明如何使用協(xié)程代替線程實(shí)現(xiàn)并發(fā)的活動(dòng)。

在仿真領(lǐng)域,進(jìn)程這個(gè)術(shù)語(yǔ)指代模型中某個(gè)實(shí)體的活動(dòng),與操作系統(tǒng)中的進(jìn)程無(wú)關(guān)。仿真系統(tǒng)中的一個(gè)進(jìn)程可以使用操作系統(tǒng)中的一個(gè)進(jìn)程實(shí)現(xiàn),但是通常會(huì)使用一個(gè)線程或一個(gè)協(xié)程實(shí)現(xiàn)。

出租車(chē)示例

import collections

# time 字段是事件發(fā)生時(shí)的仿真時(shí)間,
# proc 字段是出租車(chē)進(jìn)程實(shí)例的編號(hào),
# action 字段是描述活動(dòng)的字符串。
Event = collections.namedtuple('Event', 'time proc action')


def taxi_process(proc_num, trips_num, start_time=0):
  """
  每次改變狀態(tài)時(shí)創(chuàng)建事件,把控制權(quán)讓給仿真器
  :param proc_num:
  :param trips_num:
  :param start_time:
  :return:
  """
  time = yield Event(start_time, proc_num, 'leave garage')

  for i in range(trips_num):
    time = yield Event(time, proc_num, 'pick up people')
    time = yield Event(time, proc_num, 'drop off people')

  yield Event(time, proc_num, 'go home')

# run
t1 = taxi_process(1, 1)
a = next(t1)  
print(a)  # Event(time=0, proc=1, action='leave garage')
b = t1.send(a.time + 6)
print(b)  # Event(time=6, proc=1, action='pick up people')
c = t1.send(b.time + 12)
print(c)  # Event(time=18, proc=1, action='drop off people')
d = t1.send(c.time + 1)
print(d)  # Event(time=19, proc=1, action='go home')

模擬控制臺(tái)控制3個(gè)出租車(chē)異步

import collections
import queue
import random

# time 字段是事件發(fā)生時(shí)的仿真時(shí)間,
# proc 字段是出租車(chē)進(jìn)程實(shí)例的編號(hào),
# action 字段是描述活動(dòng)的字符串。
Event = collections.namedtuple('Event', 'time proc action')


def taxi_process(proc_num, trips_num, start_time=0):
  """
  每次改變狀態(tài)時(shí)創(chuàng)建事件,把控制權(quán)讓給仿真器
  :param proc_num:
  :param trips_num:
  :param start_time:
  :return:
  """
  time = yield Event(start_time, proc_num, 'leave garage')

  for i in range(trips_num):
    time = yield Event(time, proc_num, 'pick up people')
    time = yield Event(time, proc_num, 'drop off people')

  yield Event(time, proc_num, 'go home')


class SimulateTaxi(object):
  """
  模擬出租車(chē)控制臺(tái)
  """

  def __init__(self, proc_map):
    # 保存排定事件的 PriorityQueue 對(duì)象,
    # 如果進(jìn)來(lái)的是tuple類(lèi)型,則默認(rèn)使用tuple[0]做排序
    self.events = queue.PriorityQueue()
    # procs_map 參數(shù)是一個(gè)字典,使用dict構(gòu)建本地副本
    self.procs = dict(proc_map)

  def run(self, end_time):
    """
    排定并顯示事件,直到時(shí)間結(jié)束
    :param end_time:
    :return:
    """
    for _, taxi_gen in self.procs.items():
      leave_evt = next(taxi_gen)
      self.events.put(leave_evt)

    # 仿真系統(tǒng)的主循環(huán)
    simulate_time = 0
    while simulate_time < end_time:
      if self.events.empty():
        print('*** end of events ***')
        break

      # 第一個(gè)事件的發(fā)生
      current_evt = self.events.get()
      simulate_time, proc_num, action = current_evt
      print('taxi:', proc_num, ', at time:', simulate_time, ', ', action)

      # 準(zhǔn)備下個(gè)事件的發(fā)生
      proc_gen = self.procs[proc_num]
      next_simulate_time = simulate_time + self.compute_duration()

      try:
        next_evt = proc_gen.send(next_simulate_time)
      except StopIteration:
        del self.procs[proc_num]
      else:
        self.events.put(next_evt)
    else:
      msg = '*** end of simulation time: {} events pending ***'
      print(msg.format(self.events.qsize()))

  @staticmethod
  def compute_duration():
    """
    隨機(jī)產(chǎn)生下個(gè)事件發(fā)生的時(shí)間
    :return:
    """
    duration_time = random.randint(1, 20)
    return duration_time


# 生成3個(gè)出租車(chē),現(xiàn)在全部都沒(méi)有離開(kāi)garage
taxis = {i: taxi_process(i, (i + 1) * 2, i * 5)
     for i in range(3)}

# 模擬運(yùn)行
st = SimulateTaxi(taxis)
st.run(100)

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • django框架使用orm實(shí)現(xiàn)批量更新數(shù)據(jù)的方法

    django框架使用orm實(shí)現(xiàn)批量更新數(shù)據(jù)的方法

    這篇文章主要介紹了django框架使用orm實(shí)現(xiàn)批量更新數(shù)據(jù)的方法,結(jié)合實(shí)例形式簡(jiǎn)單分析了Django基于orm操作數(shù)據(jù)庫(kù)更新數(shù)據(jù)的相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2019-06-06
  • 簡(jiǎn)單了解python裝飾器原理及使用方法

    簡(jiǎn)單了解python裝飾器原理及使用方法

    這篇文章主要介紹了簡(jiǎn)單了解python裝飾器原理及使用方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12
  • django3.02模板中的超鏈接配置實(shí)例代碼

    django3.02模板中的超鏈接配置實(shí)例代碼

    在本篇文章里小編給大家整理了關(guān)于django3.02模板中的超鏈接配置實(shí)例代碼內(nèi)容,需要的朋友們可以學(xué)習(xí)參考下。
    2020-02-02
  • Python+QTimer計(jì)時(shí)器實(shí)現(xiàn)攝像頭視頻的播放和暫停

    Python+QTimer計(jì)時(shí)器實(shí)現(xiàn)攝像頭視頻的播放和暫停

    這篇文章主要為大家詳細(xì)介紹了Python如何通過(guò)QTimer計(jì)時(shí)器實(shí)現(xiàn)攝像頭視頻的播放和暫停功能,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-11-11
  • Python 面向?qū)ο箪o態(tài)方法、類(lèi)方法、屬性方法知識(shí)點(diǎn)小結(jié)

    Python 面向?qū)ο箪o態(tài)方法、類(lèi)方法、屬性方法知識(shí)點(diǎn)小結(jié)

    這篇文章主要介紹了Python 面向?qū)ο箪o態(tài)方法、類(lèi)方法、屬性方法,總結(jié)分析了Python 面向?qū)ο蟪绦蛟O(shè)計(jì)中靜態(tài)方法、類(lèi)方法、屬性方法相關(guān)概念、知識(shí)點(diǎn)、操作技巧與使用注意事項(xiàng),需要的朋友可以參考下
    2020-03-03
  • Python從MP3文件獲取id3的方法

    Python從MP3文件獲取id3的方法

    這篇文章主要介紹了Python從MP3文件獲取id3的方法,實(shí)例分析了Python操作文件屬性的相關(guān)技巧,需要的朋友可以參考下
    2015-06-06
  • Pandas實(shí)現(xiàn)聚合運(yùn)算agg()的示例代碼

    Pandas實(shí)現(xiàn)聚合運(yùn)算agg()的示例代碼

    在數(shù)據(jù)分析中,分組聚合二者缺一不可。對(duì)數(shù)據(jù)聚合(求和、平均值等)通常是不可避免的。pd.agg()很方便進(jìn)行聚合操作。本文就來(lái)介紹一下,感興趣的可以了解一下
    2021-07-07
  • Python函數(shù)參數(shù)操作詳解

    Python函數(shù)參數(shù)操作詳解

    這篇文章主要介紹了Python函數(shù)參數(shù)操作,結(jié)合實(shí)例形式詳細(xì)分析了Python形參、實(shí)參、默認(rèn)參數(shù)、關(guān)鍵字參數(shù)、可變參數(shù)、對(duì)參數(shù)解包以及獲取參數(shù)個(gè)數(shù)等相關(guān)操作技巧,需要的朋友可以參考下
    2018-08-08
  • Python WebSocket長(zhǎng)連接心跳與短連接的示例

    Python WebSocket長(zhǎng)連接心跳與短連接的示例

    這篇文章主要介紹了Python WebSocket長(zhǎng)連接心跳與短連接的示例,幫助大家更好的理解和學(xué)習(xí)python,感興趣的朋友可以了解下
    2020-11-11
  • 如何用Python做一個(gè)微信機(jī)器人自動(dòng)拉群

    如何用Python做一個(gè)微信機(jī)器人自動(dòng)拉群

    這篇文章主要介紹了如何用Python做一個(gè)微信機(jī)器人自動(dòng)拉群,微當(dāng)群人數(shù)達(dá)到100人后,用戶(hù)無(wú)法再通過(guò)掃描群二維碼加入,只能讓用戶(hù)先添加群內(nèi)聯(lián)系人微信,再由聯(lián)系人把用戶(hù)拉進(jìn)來(lái)。這樣,聯(lián)系人員的私人微信會(huì)添加大量陌生人,給其帶來(lái)不必要的打擾,需要的朋友可以參考下
    2019-07-07

最新評(píng)論

类乌齐县| 平定县| 嘉峪关市| 贵德县| 增城市| 广南县| 岑巩县| 文成县| 萨迦县| 金堂县| 鄂伦春自治旗| 阳信县| 沂南县| 永春县| 双柏县| 达尔| 建始县| 微山县| 黄骅市| 齐河县| 万安县| 郁南县| 呼玛县| 商水县| 临沭县| 牙克石市| 平湖市| 阳高县| 永仁县| 新津县| 宁化县| 辛集市| 石泉县| 英吉沙县| 东阿县| 高碑店市| 大同市| 东兰县| 赤壁市| 哈尔滨市| 都江堰市|