這篇文章是讀者朋友的python協程的學習經驗之談,以下是全部內容:
協程的歷史說來話長,要從生成器開始講起。
如果你看過我之前的文章python奇遇記:迭代器和生成器 ,對生成器的概念應該很了解。生成器節省內存,用的時候才生成結果。
# 生成器表達式a = (x*x for x in range(10))# next生成值next(a()) # 輸出0next(a()) # 輸出1next(a()) # 輸出4
與生成器產出數據不同的是,協程在產出數據的同時還可以接收數據,具體來說就是把yield 放在了表達式的右邊。我們可以使用.send() 把數據發送給協程函數。
def writer(): print('-> coroutine started') for i in range(8): w = yield print(i+w)w = writer()# 本質還是生成器>>> w<generator object writer at 0x000002595BC57468># 首先要用next()把協程激活>>> next(w)-> coroutine started# 發送數據>>> w.send(1)1# send到第八次之后會拋出異常# 因為協程已經結束了---------------------------------------------------------------------------StopIteration Traceback (most recent call last)第一步必須使用next() 激活協程函數,這樣才能在下一步使用.send() 發送數據。
可以看到,在第8次接收完數據之后,會產生結束的異常,因為程序流程結束了,這是正常現象。加個異常處理即可。如果需要在兩個協程間傳遞數據呢?
def writer(): while True: w = yield print('>>', w)def writer_wrapper(coro): # 激活 next(coro) while True: # 異常處理 try: x = yield # 發送數據給writer coro.send(x) except StopIteration: passw = writer()wrap = writer_wrapper(w)# 激活next(wrap)for i in range(4): wrap.send(i)# 輸出>> 0>> 1>> 2>> 3上面的代碼中,數據首先傳遞到writer_wrapper,之后再傳遞到writer 。
data——>writer_wrapper——>writer
可以這么寫,不過,又要預先激活,又要加異常,看起來有點麻煩啊。yield from 的出現可以解決這個問題,同樣是傳遞數據:
def writer(): while True: w = yield print('>>', w)def writer_wrapper2(coro): yield from coro一行代碼解決問題。
總之,yield from相當于提供了一個通道,使得數據可以在協程之間流轉 。writer_wrapper2 中使用yield from coro時,coro此時獲得控制權,在我們.send() 數據時,writer_wrapper2 被阻塞,直到writer 打印出結果。
在這個階段,協程本質上還是由生成器構成的。
即使我們使用yield from 簡化了流程,協程和生成器的知識理解起來還是有點懵逼,而且yield from 用在異步編程中有諸多不順(asyncio以前就是用yield from),于是在3.5版本的python中,棄用了yield from ,新加入了兩個關鍵字async 和await ,同時協程不再是生成器類型,而是原生的協程類型。
新聞熱點
疑難解答