python協程的實現經歷了三個階段:生成器、基于生成器的協程和async/await。最初,生成器通過yield和.send()實現了基本的協程功能,具備暫停、恢復及雙向通信能力;隨后,@asyncio.coroutine裝飾器配合yield from實現了異步任務調度,但仍存在語法不直觀、需手動管理事件循環等問題;最終,python 3.5引入async/await語法,async def定義協程函數,await替代yield from,語義更清晰,協程標識更明確,同時Event loop也逐步簡化并模塊化,提升了異步編程效率與性能。
在 Python 中,協程(coroutine)的實現并不是一蹴而就的,而是隨著語言的發展逐步演進而來。從最初的生成器(generator)到如今我們熟悉的 async/await 語法,整個過程可以說是 Python 異步編程的一段進化史。
生成器:協程的基礎
Python 的協程最早是基于生成器構建的。生成器由 yield 關鍵字引入,在早期版本中主要用于惰性地生成數據流。
但人們很快發現,yield 不僅可以用來“產出”值,還可以通過 .send() 方法接收外部傳入的值,這使得生成器具備了“雙向通信”的能力。于是,生成器變成了可以暫停、恢復執行的函數,這就是協程的雛形。
舉個簡單的例子:
立即學習“Python免費學習筆記(深入)”;
def simple_coroutine(): x = yield print("Received:", x) coro = simple_coroutine() next(coro) # 啟動協程 coro.send(42) # 輸出 Received: 42
這個例子展示了協程的基本行為:暫停和恢復,并且能接收外部輸入。雖然看起來簡單,但正是這種機制為后來的異步編程打下了基礎。
基于生成器的協程:手動調度時代
當開發者開始用生成器模擬協程時,出現了像 @asyncio.coroutine 這樣的裝飾器,它本質上就是把一個生成器標記為協程。
這時候的協程需要手動調度,比如使用 yield from 來嵌套調用其他協程:
@asyncio.coroutine def sub_coroutine(): print("Start sub") yield from asyncio.sleep(1) print("End sub") @asyncio.coroutine def main(): yield from sub_coroutine() asyncio.get_event_loop().run_until_complete(main())
這種方式雖然可行,但有幾個問題:
- 語法不夠直觀,yield from 容易讓人誤解為只是生成器嵌套
- 需要手動管理事件循環和調度邏輯
- 協程和普通生成器沒有明顯區分,容易出錯
async/await:現代協程的誕生
為了改進這些問題,Python 3.5 引入了 async/await 語法,標志著現代協程的正式登場。
- async def 定義協程函數
- await 用于等待另一個協程完成,代替了之前的 yield from
上面的例子改寫成 async/await 版本如下:
async def sub_coroutine(): print("Start sub") await asyncio.sleep(1) print("End sub") async def main(): await sub_coroutine() asyncio.run(main())
這樣寫的好處很明顯:
- 語義清晰,await 明確表示等待一個協程
- 協程函數有明確標識(async def),避免與普通函數混淆
- 更加符合異步編程的思維習慣,也更容易被工具識別和優化
小細節:event loop 的變化也很關鍵
在 async/await 初期,還需要手動獲取和運行 event loop,比如:
loop = asyncio.get_event_loop() loop.run_until_complete(main())
但從 Python 3.7 開始,可以直接使用 asyncio.run(main()),內部自動創建并關閉 event loop,簡化了使用流程。
另外,event loop 的實現也逐漸模塊化,允許第三方庫自定義(如 uvloop),這也讓 Python 的異步性能有了很大提升空間。
總結一下發展脈絡
- 生成器階段:利用 yield 和 .send() 實現基本的協程功能
- 生成器協程階段:通過 @asyncio.coroutine 和 yield from 構建異步任務
- async/await 階段:語法層面支持協程,更清晰、安全、高效
基本上就這些內容了。理解這段歷史,不僅能幫助你更好地寫出高質量的異步代碼,也能讓你明白為什么現在推薦用 async/await 而不是老式的生成器方式。