Python 簡易手冊

單元 53 - 協程

協程 (coroutine) 是指暫停目前執行的程式 (program) ,等待另一個程式執行完畢,然後才重啟原本暫停的程式繼續執行,這需要用到標準程式庫 (standard library) 中的 asyncio ,然後用關鍵字 (keyword) async 定義協程函數 (function) ,等待要先執行的工作則是放在 await 陳述 (statement) ,簡單舉例如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 從標準程式庫引入 asyncio
import asyncio

# 定義協程函數
async def delay_print():
    # 暫停 10 秒
    await asyncio.sleep(10)
    # 印出訊息
    print("Something happened...")

# 執行協程函數
asyncio.run(delay_print())

# 檔名: async_demo.py
# 說明: 《Python簡易手冊》的範例
# 網址: http://kaiching.org
# 作者: Kaiching Chang
# 時間: 2024 年 3 月

單元 44 - 函數與 return 陳述介紹如何定義函數。

第 2 行先引入標準程式庫中的 asyncio

 1
 2
# 從標準程式庫引入 asyncio
import asyncio

第 5 行定義協程函數

 4
 5
 6
 7
 8
 9
# 定義協程函數
async def delay_print():
    # 暫停 10 秒
    await asyncio.sleep(10)
    # 印出訊息
    print("Something happened...")

注意第 7 行是 await 陳述,關鍵字 await 之後可以放任何要先執行的程式碼,協程函數執行到這裡,會先暫停,然後執行 await 之後的程式碼,等到 await 之後的程式碼執行完畢,才會回到協程函數繼續執行下一行程式碼

 6
 7
    # 暫停 10 秒
    await asyncio.sleep(10)

上面 await 之後為 asyncio.sleep(10) ,就字面上意義是睡覺,在程式是指不做任何事情,引數 (argument) 10 是指 10 秒,所以這裡是單純等待 10 秒。

底下執行部分需要把協程函數 delay_print() 放在 asyncio.run() 中呼叫

11
12
# 執行協程函數 
asyncio.run(delay_print())

執行結果就是會先等待

> python async_demo.py

10 秒後印出訊息

Something happened...
>

如果協程函數中用到關鍵字 yield ,這會是協程產生器 (async generator) ,要在另一個協程函數用 async for 依序取得協程產生器產生的值,舉例如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 從標準程式庫引入 asyncio
import asyncio

# 定義協程產生器函數
async def do_something():
    for i in range(10):
        if i % 2 != 0:
            yield i

# 定義協程函數
async def main():
    async for i in do_something():
        await asyncio.sleep(1)
        print(i)

# 執行協程函數
asyncio.run(main())

# 檔名: async_demo2.py
# 說明: 《Python簡易手冊》的範例
# 網址: http://kaiching.org
# 作者: Kaiching Chang
# 時間: 2024 年 3 月

單元 50 - 產生器介紹如何定義產生器 (generator) 。

async for 在第 12 行,暫停一秒然後印出產生的數字

10
11
12
13
14
# 定義協程函數
async def main():
    async for i in do_something():
        await asyncio.sleep(1)
        print(i)

注意協程產生器不能用 next() 取值,完整執行結果如下

> python async_demo2.py
1
3
5
7
9
>

如果有多個協程任務,這時可以用 async with 來處理,舉例如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 從標準程式庫引入 asyncio
import asyncio

# 定義協程函數
async def print_after(delay, what):
    await asyncio.sleep(delay)
    print(what)

# 定義執行多個協程任務的函數
async def main():
    async with asyncio.TaskGroup() as tg:
        task1 = tg.create_task(
            print_after(2, '2 seconds...'))

        task2 = tg.create_task(
            print_after(5, '5 seconds...'))

# 執行協程函數
asyncio.run(main())

# 檔名: async_demo3.py
# 說明: 《Python簡易手冊》的範例
# 網址: http://kaiching.org
# 作者: Kaiching Chang
# 時間: 2024 年 3 月

單元 28 - with 陳述介紹如何關鍵字 with 的用法。

async with 在第 11 行,後面是接 asyncio.TaskGroup() ,這是做協程任務管理的物件,所以底下要用 create_task() 方法建立協程任務,這裡是執行後 2 秒印出第一次訊息, 5 秒印出第二次訊息,完整執行結果如下

> python async_demo3.py
2 seconds...
5 seconds...
>

參考資料

上一頁 單元 52 - global 陳述與 nonlocal 陳述
回 Python 簡易手冊 首頁
下一頁 單元 54 - 類別
回 Python 教材首頁
回程式語言教材首頁