協程與yield表示式

2021-06-01 05:45:35 字數 2888 閱讀 2762

在函式內,yield語句還可以用作出現在賦值運算子右邊的表示式,例如:

def receiver():

print "ready to receive"

while true:

n = (yield)

print "got %s" % n

以這種方式使用yield語句的函式稱為協程,它的執行是為了響應傳送給它的值。它的行為也十分類似於生成器,例如:

>>> r = receiver()

>>> r.next() #向前執行到第一條yield語句(在python3中是r.__next__())

ready to receive

>>> r.send(1)

got 1

>>> r.send(2)

got 2

>>> r.send("hello")

got hello

>>>

在這個例子中,對next()的初始呼叫是必不可少的,這樣協程才能執行可通向第乙個yield表示式的語句。在這裡協程會掛起,等待相關生成器物件r的send()方法給它傳送乙個值。傳遞給send()的值由協程中的(yield)表示式返回。接收到值後,協程就會執行語句,直至遇到下一條yield語句。

在協程中需要呼叫next()這件事情很容易被忽略,經常成為錯誤出現的根源。因此,建議使用乙個能自動完成該步驟的裝飾器來包裝協程。

def coroutine(func):

def start(*args, **kwargs)

g = func(*args, **kwargs)

g.next()

return g

return start

使用這個裝飾器就可以像下面這樣程式設計和使用協程:

@coroutine

def receiver():

print "ready to receive"

while true:

n = (yield)

print "got %s" % n

#用例r = receiver()

r.send("hello world") # 注意:無需初始呼叫.next()方法

協程的執行一般是無限期的,除非它被顯式關閉或者自己退出。使用方法close()可以關閉輸入值的流,例如:

>>>r.close()

>>>r.send(4)

traceback (most recent call last):

file , line 1, in stopiteration

關閉後,如果繼續給協程傳送值,就會引發stopiteration異常。正如前面關於生成器的內容中講到的那樣,close()操作將在協程內部引發generatorexit異常,例如:

def receiver():

print "ready to receive"

try:

while true:

n = (yield)

print "got %s" % n

except generatorexit:

print "receiver done"

可以使用throw(exctype [,value[, tb]])方法在協程內部引發異常,其中exctype是指異常型別,value是指異常的值,而tb是指跟蹤物件。例如:

>>> r.throw(runtimeerror, "you're hosted!")

traceback (most recent call last):

file , line 1, in file , line 4, in receiver

runtimeerror:you're hosted!

以這種方式引發的異常在協程中當前執行的yield語句處出現。協程可以選擇捕捉異常並以正確方式處理它們。使用throw()方法作為給協程的非同步訊號並不安全——應該禁止從單獨的執行執行緒或訊號處理程式呼叫這個方法。

如果yield表示式中提供了值,協程可以使用yield語句同時接收和發出返回值,例如:

def line_splitter(delimiter=none):

print("ready to split")

result = none

while true:

line = (yield result)

result = line.split(delimiter)

在這個例子中,我們使用協程的方式與前面相同。但是,現在呼叫send()方法也會生成乙個結果,例如:

>>> s = line_splitter(",")

>>> s.next()

ready to split

>>> s.send("a,b,c")

['a', 'b', 'c']

>>> s.send("100,200,300")

['100','200','300']

>>>

理解這個例子中的先後順序至關重要。首個next()呼叫讓協程向前執行到(yield result),這將返回result的初始值none。在接下來的send()呼叫中,接收到的值被放在line中並拆分到result中。send()方法的返回值就是傳遞給下一條yield語句的值。換句話說,send()方法的返回值來自下乙個yield表示式,而不是接收send()傳遞的值的yield表示式。

如果協程返回值,對使用throw()引發的異常進行處理時需要當心。如果使用throw()在協程中引發乙個異常,傳遞給協程中下一條yield語句的值將作為throw()方法的結果返回。如果需要這個值卻又忘記儲存它,它就會消失不見。

yield實現 coroutine協程案例

yield可以手工實現協程,但python為我們封裝了乙個greenlet,先看看yield實現,yield需要手工操作,無法實現io操作時自動切換協程,greenlet是封裝好的,能方便使用io切換!importtime importqueue defconsumer name print sta...

python協程與非同步協程

在前面幾個部落格中我們一一對應解決了消費者消費的速度跟不上生產者,浪費我們大量的時間去等待的問題,在這裡,針對業務邏輯比較耗時間的問題,我們還有除了多程序之外更優的解決方式,那就是協程和非同步協程。在引入這個概念之前我們先看 看這個圖 從這個我們可以看出來,假如來了9個任務,即使我們開了多程序,在業...

Python基礎 協程 yield關鍵字

乙個執行緒寫訊息,乙個執行緒取訊息,通過鎖機制控制佇列和等待,但一不小心就可能死鎖 生產者生產訊息後,直接通過yield跳轉到消費者開始執行,待消費者執行完畢後,切換回生產者繼續生產,效率極高 main.py usr bin env python3 coding utf 8 python基礎 協程 ...