午夜视频免费看_日韩三级电影网站_国产精品久久一级_亚洲一级在线播放_人妻体内射精一区二区三区_91夜夜揉人人捏人人添红杏_91福利在线导航_国产又粗又猛又黄又爽无遮挡_欧美日韩一区在线播放_中文字幕一区二区三区四区不卡 _日日夜夜精品视频免费观看_欧美韩日一区二区三区

主頁 > 知識(shí)庫 > 深入理解python協(xié)程

深入理解python協(xié)程

熱門標(biāo)簽:北京電信外呼系統(tǒng)靠譜嗎 無錫客服外呼系統(tǒng)一般多少錢 地圖標(biāo)注視頻廣告 老人電話機(jī)器人 高德地圖標(biāo)注是免費(fèi)的嗎 大連crm外呼系統(tǒng) 洪澤縣地圖標(biāo)注 百度地圖標(biāo)注位置怎么修改 梅州外呼業(yè)務(wù)系統(tǒng)

一、什么是協(xié)程

協(xié)程擁有自己的寄存器和棧。協(xié)程調(diào)度切換的時(shí)候,將寄存器上下文和棧都保存到其他地方,在切換回來的時(shí)候,恢復(fù)到先前保存的寄存器上下文和棧,因此:協(xié)程能保留上一次調(diào)用狀態(tài),每次過程重入時(shí),就相當(dāng)于進(jìn)入上一次調(diào)用的狀態(tài)。

協(xié)程的好處:

  • 1.無需線程上下文切換的開銷(還是單線程)
  • 2.無需原子操作(一個(gè)線程改一個(gè)變量,改一個(gè)變量的過程就可以稱為原子操作)的鎖定和同步的開銷
  • 3.方便切換控制流,簡化編程模型
  • 4.高并發(fā)+高擴(kuò)展+低成本:一個(gè)cpu支持上萬的協(xié)程都沒有問題,適合用于高并發(fā)處理

缺點(diǎn):

  • 1.無法利用多核的資源,協(xié)程本身是個(gè)單線程,它不能同時(shí)將單個(gè)cpu的多核用上,協(xié)程需要和進(jìn)程配合才能運(yùn)用到多cpu上(協(xié)程是跑在線程上的)
  • 2.進(jìn)行阻塞操作時(shí)會(huì)阻塞掉整個(gè)程序:如io

二、了解協(xié)程的過程

2.1、yield工作原理

從語法上來看,協(xié)程和生成器類似,都是定義體中包含yield關(guān)鍵字的函數(shù)。

yield在協(xié)程中的用法:

  • 在協(xié)程中yield通常出現(xiàn)在表達(dá)式的右邊,例如:datum = yield,可以產(chǎn)出值,也可以不產(chǎn)出--如果yield關(guān)鍵字后面沒有表達(dá)式,那么生成器產(chǎn)出None。
  • 在協(xié)程中yield也可能從調(diào)用方接受數(shù)據(jù),調(diào)用方是通過send(datum)的方式把數(shù)據(jù)提供給協(xié)程使用,而不是next(...)函數(shù),通常調(diào)用方會(huì)把值推送給協(xié)程。
  • 協(xié)程可以把控制器讓給中心調(diào)度程序,從而激活其他的協(xié)程。

所以總體上在協(xié)程中把yield看做是控制流程的方式。

先通過一個(gè)簡單的協(xié)程的例子理解:

def simple_demo():
    print("start")
    x = yield
    print("x:", x)

sd = simple_demo()
next(sd)
sd.send(10)

---------------------------

>>> start
>>> x: 10
>>> Traceback (most recent call last):
>>>   File "D:/python_projects/untitled3/xiecheng1.py", line 9, >>> in module>
>>>     sd.send(10)
>>> StopIteration

對(duì)上述例子的分析:

yield 的右邊沒有表達(dá)式,所以這里默認(rèn)產(chǎn)出的值是None
剛開始先調(diào)用了next(...)是因?yàn)檫@個(gè)時(shí)候生成器還沒有啟動(dòng),沒有停在yield那里,這個(gè)時(shí)候也是無法通過send發(fā)送數(shù)據(jù)。所以當(dāng)我們通過next(...)激活協(xié)程后,程序就會(huì)運(yùn)行到x = yield,這里有個(gè)問題我們需要注意,x = yield這個(gè)表達(dá)式的計(jì)算過程是先計(jì)算等號(hào)右邊的內(nèi)容,然后在進(jìn)行賦值,所以當(dāng)激活生成器后,程序會(huì)停在yield這里,但并沒有給x賦值。

當(dāng)我們調(diào)用send方法后yield會(huì)收到這個(gè)值并賦值給x,而當(dāng)程序運(yùn)行到協(xié)程定義體的末尾時(shí)和用生成器的時(shí)候一樣會(huì)拋出StopIteration異常

如果協(xié)程沒有通過next(...)激活(同樣我們可以通過send(None)的方式激活),但是我們直接send,會(huì)提示如下錯(cuò)誤:

def simple_demo():
    print("start")
    x = yield
    print("x:", x)

sd = simple_demo()
# next(sd)
sd.send(10)

---------------------------

>>> Traceback (most recent call last):
>>>   File "D:/python_projects/untitled3/xiecheng1.py", line 9, >>> in module>
>>>     sd.send(10)
>>> TypeError: can't send non-None value to a just-started generator

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

協(xié)程在運(yùn)行過程中有四個(gè)狀態(tài):

  • GEN_CREATE:等待開始執(zhí)行
  • GEN_RUNNING:解釋器正在執(zhí)行,這個(gè)狀態(tài)一般看不到
  • GEN_SUSPENDED:在yield表達(dá)式處暫停
  • GEN_CLOSED:執(zhí)行結(jié)束

通過下面例子來查看協(xié)程的狀態(tài):

>>> from inspect import getgeneratorstate
>>> def simple_demo(a):
    print("start: a = ", a)
    b = yield a
    print("b = ", b)
    c = yield a + b
    print("c = ", c)

    
>>> sd = simple_demo(2)
>>> print(getgeneratorstate(sd))
GEN_CREATED
>>> next(sd)  # 預(yù)激協(xié)程,使它走到第一個(gè)yield處,因?yàn)榈谝粋€(gè)yield處有yield值a,所以返回a的值,然后在此yield處阻塞
start: a =  2
2
>>> print(getgeneratorstate(sd))
GEN_SUSPENDED
>>> sd.send(3) # 發(fā)送3,進(jìn)入?yún)f(xié)程接著上一次阻塞的yield處執(zhí)行,yield接收參數(shù)3賦值給b,到下一個(gè)yield處返回a+b的值,然后在此yield處再次阻塞,等待下次send值
b =  3
5
>>> sd.send(4) # 同上一次send過程,到此結(jié)束拋異常
c =  4
Traceback (most recent call last):
  File "pyshell#8>", line 1, in module>
    sd.send(4)
StopIteration
>>> print(getgeneratorstate(sd))
GEN_CLOSED

可以通過注釋理解這個(gè)例子。

接著再通過一個(gè)計(jì)算平均值的例子來繼續(xù)理解:

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

		
>>> avg = averager()
>>> next(avg)
>>> avg.send(10)
10.0
>>> avg.send(30)
20.0
>>> avg.send(40)
26.666666666666668

這里是一個(gè)死循環(huán),只要不停send值給協(xié)程,可以一直計(jì)算下去。
通過上面的幾個(gè)例子我們發(fā)現(xiàn),我們?nèi)绻胍_始使用協(xié)程的時(shí)候必須通過next(...)方式激活協(xié)程,如果不預(yù)激,這個(gè)協(xié)程就無法使用,如果哪天在代碼中遺忘了那么就出問題了,所以有一種預(yù)激協(xié)程的裝飾器,可以幫助我們干這件事。

2.2、預(yù)激協(xié)程的裝飾器

下面是預(yù)激裝飾器的演示例子:

from functools import wraps

def coroutine(func):
    @wraps(func)
    def primer(*args,**kwargs):
        gen = func(*args,**kwargs)
        next(gen)
        return gen
    return primer

@coroutine
def averager():
    total = 0.0
    count = 0
    average = None
    while True:
        term = yield average
        total += term
        count += 1
        average = total/count

coro_avg = averager()
from inspect import getgeneratorstate
print(getgeneratorstate(coro_avg))
print(coro_avg.send(10))
print(coro_avg.send(30))
print(coro_avg.send(5))

---------------------------

>>> GEN_SUSPENDED
>>> 10.0
>>> 20.0
>>> 15.0

關(guān)于預(yù)激,在使用yield from句法調(diào)用協(xié)程的時(shí)候,會(huì)自動(dòng)預(yù)激活,這樣其實(shí)與我們上面定義的coroutine裝飾器是不兼容的,在python3.4里面的asyncio.coroutine裝飾器不會(huì)預(yù)激協(xié)程,因此兼容yield from

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

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

繼續(xù)使用上面averager的例子

>>> coro_avg = averager()
>>> coro_avg.send(40)
40.0
>>> coro_avg.send(50)
45.0
>>> coro_avg.send('spam')
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for +=: 'float' and 'str'
>>> coro_avg.send(60)
Traceback (most recent call last):
File "stdin>", line 1, in module>
StopIteration

由于在協(xié)程內(nèi)沒有處理異常,協(xié)程會(huì)終止。如果試圖重新激活協(xié)程,會(huì)拋出StopIteration 異常。

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

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

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

2:generator.close()

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

示例如下:

from inspect import getgeneratorstate
class DemoException(Exception):
    """為這次演示定義的異常類型。"""
    pass
    
def demo_exc_handling():
    print('-> coroutine started')
    while True:
        try:
            x = yield
        except DemoException:
            print('*** DemoException handled. Continuing...')
        else:
            print('-> coroutine received: {!r}'.format(x))
    raise RuntimeError('This line should never run.')
    
>>> exc_coro = demo_exc_handling()
>>> next(exc_coro)
-> coroutine started
>>> exc_coro.send(11)
-> coroutine received: 11
>>> exc_coro.send(22)
-> coroutine received: 22

>>> exc_coro.throw(DemoException)
*** DemoException handled. Continuing...
>>> getgeneratorstate(exc_coro)
'GEN_SUSPENDED'
>>> exc_coro.close()
>>> getgeneratorstate(exc_coro)
'GEN_CLOSED'

2.4、讓協(xié)程返回值

在Python2中,生成器函數(shù)中的return不允許返回附帶返回值。在Python3中取消了這一限制,因而允許協(xié)程可以返回值:

from collections import namedtuple
Result = namedtuple('Result', '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 Result(count, average)
    
>>> coro_avg = averager()
>>> next(coro_avg)
>>> coro_avg.send(10)
>>> coro_avg.send(30)
>>> coro_avg.send(6.5)
>>> coro_avg.send(None)
Traceback (most recent call last):
...
StopIteration: Result(count=3, average=15.5)   

發(fā)送 None 會(huì)終止循環(huán),導(dǎo)致協(xié)程結(jié)束,返回結(jié)果。一如既往,生成器對(duì)象會(huì)拋出StopIteration 異常。異常對(duì)象的 value 屬性保存著返回的值。

注意,return 表達(dá)式的值會(huì)偷偷傳給調(diào)用方,賦值給 StopIteration 異常的一個(gè)屬性。這樣做有點(diǎn)不合常理,但是能保留生成器對(duì)象的常規(guī)行為——耗盡時(shí)拋出StopIteration 異常。如果需要接收返回值,可以這樣:

>>> try:
...    coro_avg.send(None)
... except StopIteration as exc:
...    result = exc.value
...
>>> result
Result(count=3, average=15.5)

獲取協(xié)程的返回值要繞個(gè)圈子,可以使用Python3.3引入的yield from獲取返回值。yield from 結(jié)構(gòu)會(huì)在內(nèi)部自動(dòng)捕獲 StopIteration 異常。這種處理方式與 for 循環(huán)處理 StopIteration 異常的方式一樣。對(duì) yield from 結(jié)構(gòu)來說,解釋器不僅會(huì)捕獲 StopIteration 異常,還會(huì)把value 屬性的值變成 yield from 表達(dá)式的值。

2.5、yield from的使用

yield from 是 Python3.3 后新加的語言結(jié)構(gòu)。在其他語言中,類似的結(jié)構(gòu)使用 await 關(guān)鍵字,這個(gè)名稱好多了,因?yàn)樗鼈鬟_(dá)了至關(guān)重要的一點(diǎn):在生成器 gen 中使用 yield from subgen() 時(shí),subgen 會(huì)獲得控制權(quán),把產(chǎn)出的值傳給 gen 的調(diào)用方,即調(diào)用方可以直接控制 subgen。與此同時(shí),gen 會(huì)阻塞,等待 subgen 終止。

yield from 可用于簡化 for 循環(huán)中的 yield 表達(dá)式。例如:

>>> def gen():
... for c in 'AB':
...     yield c
... for i in range(1, 3):
...     yield i
...
>>> list(gen())
['A', 'B', 1, 2]

可以改為

>>> def gen():
...     yield from 'AB'
...     yield from range(1, 3)
...
>>> list(gen())
['A', 'B', 1, 2]

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

如果 yield from 結(jié)構(gòu)唯一的作用是替代產(chǎn)出值的嵌套 for 循環(huán),這個(gè)結(jié)構(gòu)很有可能不會(huì)添加到 Python 語言中。

yield from 的主要功能是打開雙向通道,把最外層的調(diào)用方與最內(nèi)層的子生成器連接起來,這樣二者可以直接發(fā)送和產(chǎn)出值,還可以直接傳入異常,而不用在位于中間的協(xié)程中添加大量處理異常的樣板代碼。有了這個(gè)結(jié)構(gòu),協(xié)程可以通過以前不可能的方式委托職責(zé)。

PEP 380 使用了一些yield from使用的專門術(shù)語:

  • 委派生成器:包含 yield from 表達(dá)式的生成器函數(shù);
  • 子生成器:從 yield from 表達(dá)式中 部分獲取的生成器;
  • 調(diào)用方:調(diào)用委派生成器的客戶端代碼;

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

下面是一個(gè)求平均身高和體重的示例代碼:

from collections import namedtuple

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

# 子生成器
def averager():
    total = 0.0
    count = 0
    average = None
    while True:
        # main 函數(shù)發(fā)送數(shù)據(jù)到這里 
        print("in averager, before yield")
        term = yield
        if term is None: # 終止條件
            break
        total += term
        count += 1
        average = total/count

    print("in averager, return result")
    return Result(count, average) # 返回的Result 會(huì)成為grouper函數(shù)中yield from表達(dá)式的值


# 委派生成器
def grouper(results, key):
     # 這個(gè)循環(huán)每次都會(huì)新建一個(gè)averager 實(shí)例,每個(gè)實(shí)例都是作為協(xié)程使用的生成器對(duì)象
    while True:
        print("in grouper, before yield from averager, key is ", key)
        results[key] = yield from averager()
        print("in grouper, after yield from, key is ", key)


# 調(diào)用方
def main(data):
    results = {}
    for key, values in data.items():
        # group 是調(diào)用grouper函數(shù)得到的生成器對(duì)象
        group = grouper(results, key)
        print("\ncreate group: ", group)
        next(group) #預(yù)激 group 協(xié)程。
        print("pre active group ok")
        for value in values:
            # 把各個(gè)value傳給grouper 傳入的值最終到達(dá)averager函數(shù)中;
            # grouper并不知道傳入的是什么,同時(shí)grouper實(shí)例在yield from處暫停
            print("send to %r value %f now"%(group, value))
            group.send(value)
        # 把None傳入groupper,傳入的值最終到達(dá)averager函數(shù)中,導(dǎo)致當(dāng)前實(shí)例終止。然后繼續(xù)創(chuàng)建下一個(gè)實(shí)例。
        # 如果沒有g(shù)roup.send(None),那么averager子生成器永遠(yuǎn)不會(huì)終止,委派生成器也永遠(yuǎn)不會(huì)在此激活,也就不會(huì)為result[key]賦值
        print("send to %r none"%group)
        group.send(None)
    print("report result: ")
    report(results)


# 輸出報(bào)告
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, 41, 42, 43, 44, 54],
    'girls;m': [1.5, 1.6, 1.8, 1.5, 1.45, 1.6],
    'boys;kg':[50, 51, 62, 53, 54, 54],
    'boys;m': [1.6, 1.8, 1.8, 1.7, 1.55, 1.6],
}

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

grouper 發(fā)送的每個(gè)值都會(huì)經(jīng)由 yield from 處理,通過管道傳給 averager 實(shí)例。grouper 會(huì)在 yield from 表達(dá)式處暫停,等待 averager 實(shí)例處理客戶端發(fā)來的值。averager 實(shí)例運(yùn)行完畢后,返回的值綁定到 results[key] 上。while 循環(huán)會(huì)不斷創(chuàng)建 averager 實(shí)例,處理更多的值。

外層 for 循環(huán)重新迭代時(shí)會(huì)新建一個(gè) grouper 實(shí)例,然后綁定到 group 變量上。前一個(gè) grouper 實(shí)例(以及它創(chuàng)建的尚未終止的 averager 子生成器實(shí)例)被垃圾回收程序回收。

代碼結(jié)果如下:

create group:  generator object grouper at 0x7f34ce8458e0>

in grouper, before yield from averager, key is  girls;kg

in averager, before yield

pre active group ok

send to generator object grouper at 0x7f34ce8458e0> value 40.000000 now

in averager, before yield

send to generator object grouper at 0x7f34ce8458e0> value 41.000000 now

in averager, before yield

send to generator object grouper at 0x7f34ce8458e0> value 42.000000 now

in averager, before yield

send to generator object grouper at 0x7f34ce8458e0> value 43.000000 now

in averager, before yield

send to generator object grouper at 0x7f34ce8458e0> value 44.000000 now

in averager, before yield

send to generator object grouper at 0x7f34ce8458e0> value 54.000000 now

in averager, before yield

send to generator object grouper at 0x7f34ce8458e0> none

in averager, return result

in grouper, after yield from, key is  girls;kg

in grouper, before yield from averager, key is  girls;kg

in averager, before yield

create group:  generator object grouper at 0x7f34ce845678>

in grouper, before yield from averager, key is  girls;m

in averager, before yield

pre active group ok

send to generator object grouper at 0x7f34ce845678> value 1.500000 now

in averager, before yield

send to generator object grouper at 0x7f34ce845678> value 1.600000 now

in averager, before yield

send to generator object grouper at 0x7f34ce845678> value 1.800000 now

in averager, before yield

send to generator object grouper at 0x7f34ce845678> value 1.500000 now

in averager, before yield

send to generator object grouper at 0x7f34ce845678> value 1.450000 now

in averager, before yield

send to generator object grouper at 0x7f34ce845678> value 1.600000 now

in averager, before yield

send to generator object grouper at 0x7f34ce845678> none

in averager, return result

in grouper, after yield from, key is  girls;m

in grouper, before yield from averager, key is  girls;m

in averager, before yield

create group:  generator object grouper at 0x7f34ce845620>

in grouper, before yield from averager, key is  boys;kg

in averager, before yield

pre active group ok

send to generator object grouper at 0x7f34ce845620> value 50.000000 now

in averager, before yield

send to generator object grouper at 0x7f34ce845620> value 51.000000 now

in averager, before yield

send to generator object grouper at 0x7f34ce845620> value 62.000000 now

in averager, before yield

send to generator object grouper at 0x7f34ce845620> value 53.000000 now

in averager, before yield

send to generator object grouper at 0x7f34ce845620> value 54.000000 now

in averager, before yield

send to generator object grouper at 0x7f34ce845620> value 54.000000 now

in averager, before yield

send to generator object grouper at 0x7f34ce845620> none

in averager, return result

in grouper, after yield from, key is  boys;kg

in grouper, before yield from averager, key is  boys;kg

in averager, before yield

create group:  generator object grouper at 0x7f34ce8458e0>

in grouper, before yield from averager, key is  boys;m

in averager, before yield

pre active group ok

send to generator object grouper at 0x7f34ce8458e0> value 1.600000 now

in averager, before yield

send to generator object grouper at 0x7f34ce8458e0> value 1.800000 now

in averager, before yield

send to generator object grouper at 0x7f34ce8458e0> value 1.800000 now

in averager, before yield

send to generator object grouper at 0x7f34ce8458e0> value 1.700000 now

in averager, before yield

send to generator object grouper at 0x7f34ce8458e0> value 1.550000 now

in averager, before yield

send to generator object grouper at 0x7f34ce8458e0> value 1.600000 now

in averager, before yield

send to generator object grouper at 0x7f34ce8458e0> none

in averager, return result

in grouper, after yield from, key is  boys;m

in grouper, before yield from averager, key is  boys;m

in averager, before yield

report result: 

 6 boys  averaging 54.00kg

 6 boys  averaging 1.68m

 6 girls averaging 44.00kg

 6 girls averaging 1.58m

這個(gè)試驗(yàn)想表明的關(guān)鍵一點(diǎn)是,如果子生成器不終止,委派生成器會(huì)在yield from 表達(dá)式處永遠(yuǎn)暫停。如果是這樣,程序不會(huì)向前執(zhí)行,因?yàn)?yield from(與 yield 一樣)把控制權(quán)轉(zhuǎn)交給客戶代碼(即,委派生成器的調(diào)用方)了。

2.6、yield from的意義

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

PEP 380 在“Proposal”一節(jié)(https://www.python.org/dev/peps/pep-0380/#proposal)分六點(diǎn)說明了 yield from 的行為。這里幾乎原封不動(dòng)地引述,不過把有歧義的“迭代器”一詞都換成了“子生成器”,還做了進(jìn)一步說明。上面的示例闡明了下述四點(diǎn):

子生成器產(chǎn)出的值都直接傳給委派生成器的調(diào)用方(即客戶端代碼);

使用 send() 方法發(fā)給委派生成器的值都直接傳給子生成器。如果發(fā)送的值是None,那么會(huì)調(diào)用子生成器的 next() 方法。如果發(fā)送的值不是 None,那么會(huì)調(diào)用子生成器的 send() 方法。如果子生成器拋出 StopIteration 異常,那么委派生成器恢復(fù)運(yùn)行。任何其他異常都會(huì)向上冒泡,傳給委派生成器;

生成器退出時(shí),生成器(或子生成器)中的 return expr 表達(dá)式會(huì)觸發(fā)StopIteration(expr) 異常拋出;

yield from 表達(dá)式的值是子生成器終止時(shí)傳給 StopIteration 異常的第一個(gè)參數(shù)。

yield from 的具體語義很難理解,尤其是處理異常的那兩點(diǎn)。在PEP 380 中闡述了 yield from 的語義。還使用偽代碼(使用 Python 句法)演示了 yield from 的行為。

若想研究那段偽代碼,最好將其簡化,只涵蓋 yield from 最基本且最常見的用法:yield from 出現(xiàn)在委派生成器中,客戶端代碼驅(qū)動(dòng)著委派生成器,而委派生成器驅(qū)動(dòng)著子生成器。為了簡化涉及到的邏輯,假設(shè)客戶端沒有在委派生成器上調(diào)用throw(...) 或 close() 方法。而且假設(shè)子生成器不會(huì)拋出異常,而是一直運(yùn)行到終止,讓解釋器拋出 StopIteration 異常。上面示例中的腳本就做了這些簡化邏輯的假設(shè)。

下面的偽代碼,等效于委派生成器中的 RESULT = yield from EXPR 語句(這里針對(duì)的是最簡單的情況:不支持 .throw(...) 和 .close() 方法,而且只處理 StopIteration 異常):

_i = iter(EXPR) 
try:
    _y = next(_i)
except StopIteration as _e:
    _r = _e.value
else:
    while 1:
        _s = yield _y
    try:
        _y = _i.send(_s)
    except StopIteration as _e:
        _r = _e.value
        break
RESULT = _r

但是,現(xiàn)實(shí)情況要復(fù)雜一些,因?yàn)橐幚砜蛻魧?duì) throw(...) 和 close() 方法的調(diào)用,而這兩個(gè)方法執(zhí)行的操作必須傳入子生成器。此外,子生成器可能只是純粹的迭代器,不支持 throw(...) 和 close() 方法,因此 yield from 結(jié)構(gòu)的邏輯必須處理這種情況。如果子生成器實(shí)現(xiàn)了這兩個(gè)方法,而在子生成器內(nèi)部,這兩個(gè)方法都會(huì)觸發(fā)異常拋出,這種情況也必須由 yield from 機(jī)制處理。調(diào)用方可能會(huì)無緣無故地讓子生成器自己拋出異常,實(shí)現(xiàn) yield from 結(jié)構(gòu)時(shí)也必須處理這種情況。最后,為了優(yōu)化,如果調(diào)用方調(diào)用 next(...) 函數(shù)或 .send(None) 方法,都要轉(zhuǎn)交職責(zé),在子生成器上調(diào)用next(...) 函數(shù);僅當(dāng)調(diào)用方發(fā)送的值不是 None 時(shí),才使用子生成器的 .send(...) 方法。

下面的偽代碼,是考慮了上述情況之后,語句:RESULT = yield from EXPR的等效代碼:

_i = iter(EXPR)
try:
    _y = next(_i)
except StopIteration as _e:
    _r = _e.value
else:
    while 1:
        try:
            _s = yield _y
        except GeneratorExit as _e:
            try:
                _m = _i.close
            except AttributeError:
                pass
            else:
                _m()
            raise _e
        except BaseException as _e:
            _x = sys.exc_info()
            try:
                _m = _i.throw
            except AttributeError:
                raise _e
            else:
                try:
                    _y = _m(*_x)
                except StopIteration as _e:
                    _r = _e.value
                    break
        else:
            try:
                if _s is None:
                    _y = next(_i)
                else:
                    _y = _i.send(_s)
            except StopIteration as _e:
                _r = _e.value
                break
RESULT = _r

上面的偽代碼中,會(huì)預(yù)激子生成器。這表明,用于自動(dòng)預(yù)激的裝飾器與 yield from 結(jié)構(gòu)不兼容。

三、greenlet的使用

python中為實(shí)現(xiàn)協(xié)程封裝了一些非常好用的包,首先介紹greenlet的使用。

Greenlet是python的一個(gè)C擴(kuò)展,旨在提供可自行調(diào)度的‘微線程', 即協(xié)程。generator實(shí)現(xiàn)的協(xié)程在yield value時(shí)只能將value返回給調(diào)用者(caller)。 而在greenlet中,target.switch(value)可以切換到指定的協(xié)程(target), 然后yield value。greenlet用switch來表示協(xié)程的切換,從一個(gè)協(xié)程切換到另一個(gè)協(xié)程需要顯式指定。

以下例子:

from greenlet import greenlet
def test1():
    print(12)
    gr2.switch()
    print(34)

def test2():
    print(56)
    gr1.switch()
    print(78)

gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()

---------------------------

>>> 12
>>> 56
>>> 34

當(dāng)創(chuàng)建一個(gè)greenlet時(shí),首先初始化一個(gè)空的棧, switch到這個(gè)棧的時(shí)候,會(huì)運(yùn)行在greenlet構(gòu)造時(shí)傳入的函數(shù)(首先在test1中打印 12), 如果在這個(gè)函數(shù)(test1)中switch到其他協(xié)程(到了test2 打印34),那么該協(xié)程會(huì)被掛起,等到切換回來(在test2中切換回來 打印34)。當(dāng)這個(gè)協(xié)程對(duì)應(yīng)函數(shù)執(zhí)行完畢,那么這個(gè)協(xié)程就變成dead狀態(tài)。

對(duì)于greenlet,最常用的寫法是 x = gr.switch(y)。 這句話的意思是切換到gr,傳入?yún)?shù)y。當(dāng)從其他協(xié)程(不一定是這個(gè)gr)切換回來的時(shí)候,將值付給x。

import greenlet
def test1(x, y):
    z = gr2.switch(x+y)
    print 'test1 ', z

def test2(u):
    print 'test2 ', u
    gr1.switch(10)

gr1 = greenlet.greenlet(test1)
gr2 = greenlet.greenlet(test2)
print gr1.switch("hello", " world")

---------------------------

>>> 'test2 ' 'hello world'
>>> 'test1 ' 10
>>> None

上面的例子,第12行從main greenlet切換到了gr1,test1第3行切換到了gs2,然后gr1掛起,第8行從gr2切回gr1時(shí),將值(10)返回值給了 z。

使用greenlet需要注意一下三點(diǎn):

  • 第一:greenlet創(chuàng)生之后,一定要結(jié)束,不能switch出去就不回來了,否則容易造成內(nèi)存泄露
  • 第二:python中每個(gè)線程都有自己的main greenlet及其對(duì)應(yīng)的sub-greenlet ,不能線程之間的greenlet是不能相互切換的
  • 第三:不能存在循環(huán)引用,這個(gè)是官方文檔明確說明

四、gevent的使用

gevent可以自動(dòng)捕獲I/O耗時(shí)操作,來自動(dòng)切換協(xié)程任務(wù)。

import gevent

def f1():
    for i in range(5):
        print('run func: f1, index: %s ' % i)
        gevent.sleep(1)

def f2():
    for i in range(5):
        print('run func: f2, index: %s ' % i)
        gevent.sleep(1)

t1 = gevent.spawn(f1)
t2 = gevent.spawn(f2)
gevent.joinall([t1, t2])

------------------------------

>>> run func: f1, index: 0 
>>> run func: f2, index: 0 
>>> run func: f1, index: 1 
>>> run func: f2, index: 1 
>>> run func: f1, index: 2 
>>> run func: f2, index: 2 
>>> run func: f1, index: 3 
>>> run func: f2, index: 3 
>>> run func: f1, index: 4 
>>> run func: f2, index: 4 

由圖中可以看出,f1和f2是交叉打印信息的,因?yàn)樵诖a執(zhí)行的過程中,我們?nèi)藶槭褂胓event.sleep(0)創(chuàng)建了一個(gè)阻塞,gevent在運(yùn)行到這里時(shí)就會(huì)自動(dòng)切換函數(shù)切換函數(shù)。也可以在執(zhí)行的時(shí)候sleep更長時(shí)間,可以發(fā)現(xiàn)兩個(gè)函數(shù)基本是同時(shí)運(yùn)行然后各自等待。

關(guān)于協(xié)程,首先要充分理解協(xié)程的實(shí)現(xiàn)原理,然后使用現(xiàn)有的輪子greenlet和gevent時(shí)才能更加得心應(yīng)手!

以上就是深入理解python協(xié)程的詳細(xì)內(nèi)容,更多關(guān)于python協(xié)程的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

您可能感興趣的文章:
  • Python協(xié)程asyncio異步編程筆記分享
  • 淺談Python協(xié)程asyncio
  • 淺談Python協(xié)程
  • python協(xié)程用法實(shí)例分析
  • javascript實(shí)現(xiàn)鼠標(biāo)拖尾特效
  • JavaScript循環(huán)遍歷的24個(gè)方法,你都知道嗎
  • JavaScript實(shí)現(xiàn)簡單拖拽效果
  • JavaScript 數(shù)組去重詳解
  • 簡單談?wù)凧avaScript變量提升
  • Python 協(xié)程與 JavaScript 協(xié)程的對(duì)比

標(biāo)簽:怒江 長春 泉州 吉林 岳陽 安慶 洛陽 清遠(yuǎn)

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《深入理解python協(xié)程》,本文關(guān)鍵詞  深入,理解,python,協(xié)程,深入,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《深入理解python協(xié)程》相關(guān)的同類信息!
  • 本頁收集關(guān)于深入理解python協(xié)程的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    男人的天堂日韩| 欧美激情精品久久久久久小说| 国产精品h片在线播放| 日本久久久久久| 国产资源中文字幕| 国产情侣激情自拍| 久久免费视频色| 欧美国产欧美亚州国产日韩mv天天看完整| 亚洲人成电影网站色mp4| 无码人妻精品一区二区三区夜夜嗨| 亚洲美女屁股眼交3| 曰韩不卡视频| 精品国自产在线观看| 久久久天堂国产精品女人| 538任你躁在线精品视频网站| 欧美精品在线一区二区| 99久久人妻无码精品系列| 日韩欧美激情一区| 国产网站免费在线观看| 一边摸一边做爽的视频17国产| 欧美a级免费视频| 久久尤物电影视频在线观看| 国产一区二区自拍| 久久久久亚洲av片无码下载蜜桃 | 欧美日本亚洲| 成人在线综合网站| 一区视频在线播放| www欧美com| 欧美二区三区| 精品国产一区a| 亚洲国产www| 欧美深夜福利视频| 中文字幕欧美日韩| 国产乱子轮精品视频| 久久久久久久久久成人| 黄色a一级视频| av在线免费播放网址| 精品美女在线播放| 久久精品国内一区二区三区| 日本女人性视频| 成人国内精品久久久久一区| 久久精品视频网| 国产成人自拍网站| 亚洲国产另类久久久精品极度| 精品国产一区二区国模嫣然| 激情综合色综合久久| 美女久久久久久久久久| 97人人干人人| 337p亚洲精品色噜噜噜| 精品在线亚洲视频| 久久精品综合视频| 欧美日韩精品久久| 中文字幕国产精品久久| 国产亚洲成aⅴ人片在线观看| 日韩av在线播| 男人的天堂日韩| 91免费福利视频| 欧美在线免费播放| 国产精品高潮呻吟久久| 日本免费一区视频| 人妻互换一二三区激情视频| 国产在线播放不卡| 婷婷一区二区三区| 91在线你懂的| 欧美h视频在线观看| 日韩精品在线免费观看视频| 国产精品久久久久久久久免费丝袜 | 国产精品视频第一区| a级片在线视频| 91麻豆精品成人一区二区| 欧美日韩一区二区视频在线| 久久久这里只有精品视频| 91久久人澡人人添人人爽欧美| 国产99久久久国产精品| 国产一区二区在线播放视频| 亚洲中文字幕一区| 黄色大片中文字幕| 91精品久久久久久蜜桃| 欧美第一黄色网| 欧美成人午夜电影| 9人人澡人人爽人人精品| 国产精品无码久久av| 国内自拍偷拍视频| 日本成人三级| 国产日韩欧美日韩| 国内精品久久久久久久| 亚洲欧美变态国产另类| 一区二区三区在线视频免费观看| 国产成人av影院| 搡老岳熟女国产熟妇| 精品亚洲永久免费| 欧美日韩生活片| 最新国产精品自拍| 男女无套免费视频网站动漫| 亚洲福利av| 久久久久综合一区二区三区| 国产美女久久精品| 国产精品va在线| 尤物yw午夜国产精品视频明星| 884aa四虎影成人精品一区| 2017欧美狠狠色| 国产一区二区精品在线观看| 日韩一区二区视频在线| 高h视频免费观看| 亚洲免费黄色录像| 国产真人做爰毛片视频直播| 91视频免费进入| 国产在线a不卡| 欧美国产乱视频| 两个人的视频www国产精品| 中日韩美女免费视频网址在线观看 | 在线视频播放大全| 少妇特黄一区二区三区| 国产性生活毛片| 成人区人妻精品一区二| 法国空姐在线观看免费| 成人av免费电影| 国产精品自拍偷拍| 成人福利网站在线观看| 亚洲国产成人无码av在线| 欧美久久久影院| 中文字幕人妻一区二区| 91成人免费看| 久久久日本电影| 337p日本欧洲亚洲大胆精品| 亚洲成人在线免费| 99久久99久久精品国产片果冻 | 91麻豆精品国产91久久久久久久久 | 日韩精品一区二区三区三区免费| 欧美成人在线影院| 成人精品在线视频| 国产精品视频一区二区三区四区五区| 久久久久亚洲AV| 国产成人免费在线观看视频| 精品成人久久久| 欧美性极品xxxx做受| 中文字幕日韩av电影| 亚洲自拍av在线| 在线观看国产中文字幕| 国产极品国产极品| 黄色网址中文字幕| 国产欧美综合色| 国产成人免费在线观看不卡| 中文字幕精品—区二区四季| 亚洲欧洲美洲综合色网| 国产一级黄色录像| 久久久久久久久久久国产精品| 911亚洲精选| 久久久久99人妻一区二区三区| 黄色a级三级三级三级| 欧美久久久久久久久久久| 欧美性xxxx图片| 久久久久亚洲av成人片| 欧美一级淫片免费视频黄| 精品人妻一区二区三区蜜桃| 国产精品人人妻人人爽| 丰满岳乱妇国产精品一区| 久久精品国产清高在天天线| 国产在线精品免费av| 中文欧美字幕免费| 在线精品视频免费播放| 91精品麻豆日日躁夜夜躁| 69久久99精品久久久久婷婷 | 国产亚洲精品熟女国产成人| 国产精品视频看看| 国产91一区二区三区| 成年女人18级毛片毛片免费 | 曰批又黄又爽免费视频| 国产女人18毛片水真多成人如厕| 国产日韩亚洲欧美综合| 日韩一级片网址| 欧美亚洲另类视频| 日韩视频在线免费播放| www.88av| 亚洲欧美激情在线观看| 国产精品久久久久三级| 亚洲精品美女久久| 91免费国产视频| 人妻熟女一二三区夜夜爱| h色网站在线观看| 久久久久国产精品午夜一区| 国产欧美综合色| 最新中文字幕亚洲| 日韩欧美一区二区三区久久婷婷| 四虎精品一区二区| 国产福利小视频| 亚洲人妖av一区二区| 亚洲电影免费观看高清| 国产精品视频成人| 性chinese极品按摩| 中文字幕一区二区人妻视频| 中文字幕一区二区三区在线不卡| 在线丨暗呦小u女国产精品| 精品免费视频123区| 中文人妻一区二区三区| 国产黄色片免费观看| 中文字幕一区二区三区精华液| 精品精品国产国产自在线| 亚洲国产精品成人天堂| 国产女片a归国片aa| 欧美激情资源网| 欧美黄色性视频| 久久久久久蜜桃一区二区| 天天干视频在线| 亚洲成人xxx| 天天操天天干天天玩| 波多野结衣视频在线观看| 亚洲丝袜自拍清纯另类| 欧美高清视频在线观看| 午夜xxxxx| 国产在线视频一区二区三区| 日韩高清av在线| 国产 日韩 亚洲 欧美| 国产精品午夜一区二区| 亚洲第一久久影院| 99re国产视频| 免费三片在线播放| 亚洲综合激情另类小说区| 成人激情综合网| 蜜桃av.com| 亚洲视频免费看| 亚洲影院色无极综合| 国产三级aaa| 黄色成人av网| 欧美在线视频二区| 手机av免费观看| 欧美精品一区二区三区蜜桃| 国产91精品最新在线播放| 国产视频123区| 精品欧美aⅴ在线网站| 成人av免费看| 国产又大又粗又硬| 日韩成人中文电影| 99久久国产宗和精品1上映| 天天舔天天干天天操| 中文一区二区视频| 特级特黄刘亦菲aaa级| 久久久久国色av免费看影院| 国产精品久久久久久久久久小说 | 日韩在线免费高清视频| 国产内射老熟女aaaa| 亚洲国产精品狼友在线观看| 国产成人av一区二区三区在线观看| 精品国产3级a| 国产情侣第一页| 日一区二区三区| 日韩一区在线视频| 粉嫩av懂色av蜜臀av分享| 国产精品久久久久影院色老大| 91传媒视频在线观看| 天天操夜夜操视频| 亚洲国产精久久久久久 | 久久精品午夜福利| 不卡欧美aaaaa| 亚洲aaaaaa| 伊人网av在线| 在线看日韩欧美| 91精品人妻一区二区| 亚洲免费观看高清完整| 中文字幕中文字幕在线中一区高清 | 青青操免费在线视频| 欧美一区二区三区在线看| 亚洲性生活网站| 国产日本亚洲高清| 欧洲亚洲一区二区| 麻豆91在线播放免费| 国产日韩欧美在线看| 夜夜嗨aⅴ一区二区三区| 久久精品在线视频| 黄色片在线观看网站| 欧美日韩一区二区三区不卡| 麻豆av免费在线| 国产精品免费久久| 日本在线视频www色| 成人看片黄a免费看在线| 91久久精品在线| 日本熟妇成熟毛茸茸| 亚洲精品www久久久| 国产传媒国产传媒| 亚洲精品国产品国语在线| 五月天婷婷色综合| 欧美国产日本在线| 91精品中文字幕| 成人伊人精品色xxxx视频| 免费人成精品欧美精品| 97欧洲一区二区精品免费| 午夜成人免费影院| 91在线看网站| 精品午夜久久福利影院| 免费不卡亚洲欧美| 成人亚洲精品久久久久软件| 污视频在线免费观看一区二区三区 | 偷偷要91色婷婷| 亚洲欧洲一区二区福利| 激情综合一区二区三区| 精品国产综合久久| 国产一区二区三区免费看| 国产精品视频在线免费观看 | 日韩欧美一区二区三区四区五区| 蜜臀av亚洲一区中文字幕| 国产精品小说在线| 精品在线播放免费| 亚洲图片都市激情| 亚洲欧美一区二区不卡| 午夜av中文字幕| 精品国精品国产尤物美女| 欧美成人黄色网| 欧美激情videoshd| 久久裸体视频| 亚洲日本欧美在线| 亚洲另类在线一区| 亚洲精品一区二区18漫画| 日韩一级免费观看| 国产无遮无挡120秒| 8090成年在线看片午夜| 日韩精品一二三| 在线不卡视频一区二区| 亚洲精品视频一区| 日本wwwwwww| 最近2019免费中文字幕视频三| 91在线公开视频| 国产精品99久久久久久久| 91老司机福利 在线| 黄色国产精品视频| 精品无人国产偷自产在线| 免费在线观看h片| 青青久久av北条麻妃黑人| av中文字幕一区| 国产国语老龄妇女a片| 亚洲欧美va天堂人熟伦| 欧美日韩成人免费| 成人久久18免费网站麻豆 | 亚洲男人天堂av在线| 久久全国免费视频| 2023国产精品视频| xfplay5566色资源网站| 欧美精品一区二区在线播放| 三级精品在线观看| 加勒比成人在线| 欧美xxxx精品| 99中文字幕| 欧洲精品在线观看| 精品免费囯产一区二区三区| 国产精品青草久久久久福利99| 国产肉丝袜一区二区| 久久精品色妇熟妇丰满人妻| 国内精品国产三级国产在线专| 国产激情精品久久久第一区二区| 中文字幕永久免费| 4438全国亚洲精品在线观看视频| 亚洲第一色视频| 精品久久精品久久| 中文字幕在线观看一区| 一区二区三区免费观看视频| 伊人久久大香线蕉综合75| 亚洲人亚洲人成电影网站色| 老湿机69福利| 一区二区视频在线免费| 色综合视频一区中文字幕| 欧美无砖砖区免费| 日本一区二区三区免费乱视频 | 国产91在线高潮白浆在线观看| 欧美大片一区二区| 亚洲乱码日产精品bd| 国产一区二区三区黄视频 | 国产69精品久久久久99| 91精品国产综合久久香蕉麻豆| 国产凹凸在线观看一区二区| 国产又大又黑又粗免费视频| 亚洲黄色a v| 久久精品综合一区| 91国自产精品中文字幕亚洲| 欧美一个色资源| 一区二区三区在线视频观看 | 日本中文字幕在线免费观看| 亚洲视频在线播放免费| 四虎成人在线播放| 国产精品我不卡| 亚洲国产精品推荐| 亚洲三级久久久| 午夜国产在线观看| 日韩av片在线看| 国产aaa一级片| 最新精品视频| 亚洲成avwww人| 韩国女主播成人在线| 一区二区三区四区免费| 欧美亚洲精品日韩| 亚洲人成在线电影| 91女厕偷拍女厕偷拍高清| 野外性满足hd| 成人激情在线观看| 日韩欧美国产高清91| 亚洲毛片亚洲毛片亚洲毛片| 精品日产一区2区三区黄免费| 亚洲大胆人体在线| 国产三级欧美三级| 久久久久久不卡| 波多结衣在线观看| 国产aⅴ精品一区二区三区黄| 欧美精品18+| 91麻豆国产在线观看| 国产精品嫩草影院精东| 久久久久久久穴| 欧美成人第一区| 国内精品国产三级国产在线专| 亚洲欧美国产毛片在线| 日本在线不卡视频一二三区| 日韩欧美中文字幕视频| 欧美日韩一区二区三区69堂|