今天,我們要探討的是 Python 異步程式設計(Asynchronous Programming)。隨著現代應用的複雜度增加,特別是在處理大量 I/O 操作(如網路請求或文件讀寫)時,異步程式設計能夠提升效能,讓你的程式不必因為等待而停滯。這節課將讓 0 基礎的學生理解異步程式設計的概念、用法,以及如何在日常程式中應用。
同步 vs. 異步
基本異步語法:async 與 await
async 與 await。這兩個關鍵字幫助你定義異步函式並等待其結果。import asyncio
async def say_hello():
    print("Hello!")
    await asyncio.sleep(1)
    print("Hello again after 1 second!")
asyncio.run(say_hello())
在這個例子中,我們使用了 async 來定義異步函式 say_hello,並使用 await 等待一個模擬的 I/O 操作 asyncio.sleep(1)(等待 1 秒)。
異步任務的運行
asyncio.run() 來啟動異步任務。在 Python 3.7 之後,這是最簡單的方法來執行單個異步函式。多個任務的並行運行
asyncio.gather() 來實現:import asyncio
async def task1():
    print("Task 1 started")
    await asyncio.sleep(2)
    print("Task 1 finished")
async def task2():
    print("Task 2 started")
    await asyncio.sleep(1)
    print("Task 2 finished")
async def main():
    await asyncio.gather(task1(), task2())
asyncio.run(main())
在這裡,task1() 和 task2() 將並行執行。雖然 task1 要等待 2 秒,但 task2 會在這期間完成。
異步 I/O 操作應用
aiohttp 這樣的庫來異步執行網路操作。import aiohttp
import asyncio
async def fetch_data(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return await response.text()
async def main():
    url = "https://www.example.com"
    content = await fetch_data(url)
    print(content)
asyncio.run(main())
這裡我們使用了 aiohttp 來異步進行 HTTP 請求,並在獲取數據後立即處理。
異步程式設計的優勢與挑戰
await 和 async 的運作原理對初學者來說可能需要時間適應。任務一:異步任務並行運行
task1 和 task2,讓它們各自等待不同的秒數,並使用 asyncio.gather() 來同時執行它們。觀察兩個任務的運行順序。任務二:異步網路請求
aiohttp 庫,從至少兩個不同的網站發起 HTTP 請求,並同時獲取和顯示它們的數據。觀察它們的回應時間,並思考如果使用同步方法,這會花費多少時間。這些練習將幫助你熟悉 Python 的異步編程,使用 asyncio 和 aiohttp 來進行任務並行執行和異步網路請求。以下是每個任務的詳細解釋和範例代碼。
task1 和 task2,各自等待不同的秒數。asyncio.gather() 同時執行兩個任務,並觀察運行的順序。asyncio.gather() 來並行運行它們。import asyncio
# 定義異步函式 task1
async def task1():
    print("Task 1 開始")
    await asyncio.sleep(2)  # 模擬等待 2 秒
    print("Task 1 結束")
    return "Result of Task 1"
# 定義異步函式 task2
async def task2():
    print("Task 2 開始")
    await asyncio.sleep(1)  # 模擬等待 1 秒
    print("Task 2 結束")
    return "Result of Task 2"
# 定義主要的異步函式
async def main():
    # 使用 asyncio.gather() 同時執行 task1 和 task2
    results = await asyncio.gather(task1(), task2())
    print(results)
# 運行異步函式
asyncio.run(main())
async def 定義了異步函式,這些函式允許使用 await 關鍵字來等待某些操作的完成。asyncio.gather() 可以將多個異步任務一起執行,使它們並行運行。asyncio.run() 用來運行異步主函式 main()。task2 會先完成,因為它的等待時間比較短,這可以顯示異步任務如何提高效率。aiohttp 庫發起兩個不同網站的 HTTP 請求。aiohttp(如果還沒有):
pip install aiohttp
asyncio.gather() 同時執行它們。import aiohttp
import asyncio
import time
# 定義異步函式來發送 HTTP 請求
async def fetch(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            print(f"從 {url} 獲取數據中...")
            return await response.text()
# 定義主要的異步函式
async def main():
    urls = [
        "https://www.example.com",
        "https://www.httpbin.org/get"
    ]
    
    start_time = time.time()
    # 使用 asyncio.gather 同時發送請求
    responses = await asyncio.gather(fetch(urls[0]), fetch(urls[1]))
    
    for i, response in enumerate(responses):
        print(f"網站 {urls[i]} 的回應長度為: {len(response)}")
    # 計算總時間
    print(f"異步請求總耗時: {time.time() - start_time:.2f} 秒")
# 運行異步函式
asyncio.run(main())
aiohttp.ClientSession() 用來創建一個會話,用於發送和接收 HTTP 請求。async with session.get(url) 發起異步請求,並使用 await response.text() 來獲取完整的回應內容。asyncio.gather() 允許我們同時向多個網站發送請求,而不是依次等待每個請求完成。這兩個任務展示了如何使用異步編程來有效處理並行任務和網絡請求,這在現代應用程序中非常有用。
異步程式設計能夠顯著提升應用程式的效能,特別是在處理大量 I/O 操作時。透過 async 和 await 關鍵字,初學者能夠輕鬆上手異步程式設計。雖然異步的概念對 0 基礎的學生可能會稍顯複雜,但隨著實踐和理解,異步將成為日常開發中的得力工具。建議在日常程式開發中漸進地使用異步模式,從小型應用開始實踐。
希望今天的課程能讓你更清楚異步程式設計的概念並實際應用在你的 Python 程式中。