Python?中的異步上下文生成器?@asynccontextmanager詳解
Python 中的 @asynccontextmanager 裝飾器,它是異步編程中用于快速定義異步上下文管理器的工具,基于 contextlib 模塊實現(xiàn),核心作用是讓你用異步生成器(async yield) 替代手動定義異步上下文管理器的兩個魔法方法,大幅簡化代碼。
先鋪墊:異步上下文管理器的基礎(chǔ)
在異步代碼中,我們會用 async with 語句管理資源(比如異步連接數(shù)據(jù)庫、異步打開文件、異步網(wǎng)絡(luò)會話),而能被 async with 使用的對象,就是異步上下文管理器。
手動定義一個異步上下文管理器,需要實現(xiàn)兩個異步魔法方法:
__aenter__():進入async with時執(zhí)行,返回要操作的資源(比如數(shù)據(jù)庫連接);__aexit__():退出async with時執(zhí)行,負責釋放資源(比如關(guān)閉連接、處理異常)。
手動實現(xiàn)的缺點是代碼繁瑣,而 @asynccontextmanager 就是為了解決這個問題而生的。
@asynccontextmanager 核心作用
通過這個裝飾器,你可以用一個異步生成器函數(shù),直接定義出異步上下文管理器的「進入邏輯」和「退出邏輯」,無需手動實現(xiàn) __aenter__ 和 __aexit__:
- 生成器中
yield之前的代碼 → 對應(yīng)__aenter__()的邏輯(進入上下文,初始化/獲取資源); - 生成器中
yield之后的代碼 → 對應(yīng)__aexit__()的邏輯(退出上下文,釋放/清理資源); yield后面的值 → 就是async with ... as 變量中「變量」接收到的資源對象。
基本使用語法
from contextlib import asynccontextmanager
import asyncio
# 用@asynccontextmanager裝飾異步生成器函數(shù)
@asynccontextmanager
async def 異步上下文管理器名(參數(shù)):
# 1. 進入邏輯:初始化/獲取異步資源(對應(yīng)__aenter__)
資源 = 等待異步操作獲取資源()
try:
yield 資源 # 把資源返回給async with ... as 變量,暫停執(zhí)行
finally:
# 2. 退出邏輯:釋放/清理資源(對應(yīng)__aexit__,無論是否異常都會執(zhí)行)
等待異步操作釋放資源(資源)
# 調(diào)用:用async with語句
async def main():
async with 異步上下文管理器名(參數(shù)) as 資源變量:
# 操作資源
await 資源變量.異步方法()
# 運行異步主函數(shù)
asyncio.run(main())? 關(guān)鍵:必須用 try...finally 包裹 yield,確保即使上下文內(nèi)代碼拋出異常,資源清理邏輯也能執(zhí)行。
實際示例:異步文件管理器
用 @asynccontextmanager 實現(xiàn)一個簡單的異步文件操作上下文管理器(模擬異步文件IO,實際Python3.7+有原生asyncio.open):
from contextlib import asynccontextmanager
import asyncio
# 定義異步上下文管理器:異步打開/關(guān)閉文件
@asynccontextmanager
async def async_open(file_path, mode="r"):
print("進入上下文:異步打開文件")
# 模擬異步打開文件(實際是同步,僅作演示)
file = open(file_path, mode, encoding="utf-8")
try:
yield file # 返回文件對象,供async with使用
finally:
print("退出上下文:異步關(guān)閉文件")
file.close() # 清理資源:關(guān)閉文件
# 調(diào)用異步上下文管理器
async def read_file():
async with async_open("test.txt", "w") as f:
await asyncio.sleep(0.1) # 模擬異步寫操作
f.write("Hello @asynccontextmanager!")
print("文件操作完成,資源已釋放")
# 運行異步代碼
asyncio.run(read_file())執(zhí)行結(jié)果:
進入上下文:異步打開文件
退出上下文:異步關(guān)閉文件
文件操作完成,資源已釋放
可以看到:進入async with時執(zhí)行yield前的代碼,退出時執(zhí)行yield后的清理代碼。
進階示例:異步數(shù)據(jù)庫連接(模擬)
實際開發(fā)中,@asynccontextmanager 最常用在異步數(shù)據(jù)庫、異步Redis、異步網(wǎng)絡(luò)請求等場景,比如模擬異步MySQL連接:
from contextlib import asynccontextmanager
import asyncio
# 模擬異步數(shù)據(jù)庫客戶端
class AsyncMySQLClient:
async def connect(self):
print("建立異步數(shù)據(jù)庫連接")
return self
async def close(self):
print("關(guān)閉異步數(shù)據(jù)庫連接")
async def query(self, sql):
print(f"執(zhí)行異步SQL:{sql}")
return [{"id": 1, "name": "test"}]
# 定義異步數(shù)據(jù)庫連接的上下文管理器
@asynccontextmanager
async def get_mysql_conn():
# 1. 進入邏輯:建立異步連接
client = AsyncMySQLClient()
conn = await client.connect()
try:
yield conn # 返回連接對象,供業(yè)務(wù)代碼使用
finally:
# 2. 退出邏輯:關(guān)閉連接,釋放資源
await conn.close()
# 業(yè)務(wù)代碼:使用數(shù)據(jù)庫連接
async def query_data():
async with get_mysql_conn() as conn:
result = await conn.query("SELECT * FROM user")
print(f"查詢結(jié)果:{result}")
# 運行
asyncio.run(query_data())執(zhí)行結(jié)果:
建立異步數(shù)據(jù)庫連接
執(zhí)行異步SQL:SELECT * FROM user
查詢結(jié)果:[{'id': 1, 'name': 'test'}]
關(guān)閉異步數(shù)據(jù)庫連接
無論query中是否拋出異常,close都會執(zhí)行,確保數(shù)據(jù)庫連接被釋放。
關(guān)鍵注意點
- 裝飾的必須是異步生成器:函數(shù)必須加
async def,且內(nèi)部有yield(普通生成器不行,普通生成器用@contextmanager); - 異常處理:
yield處可能拋出上下文內(nèi)的異常,必須用try...finally,否則異常會跳過清理邏輯; - 與同步版的區(qū)別:同步上下文管理器用
@contextmanager(裝飾普通生成器),異步用@asynccontextmanager(裝飾異步生成器),前者配合with,后者配合async with; - 返回值:
yield 資源是可選的,如果上下文不需要返回資源,直接yield即可(async with 管理器:不帶as)。
對比:手動實現(xiàn) vs @asynccontextmanager
用手動實現(xiàn)異步上下文管理器的方式重寫上面的「異步文件管理器」,對比代碼量:
import asyncio
# 手動實現(xiàn)異步上下文管理器(需實現(xiàn)__aenter__/__aexit__)
class AsyncOpen:
def __init__(self, file_path, mode="r"):
self.file_path = file_path
self.mode = mode
self.file = None
# 異步進入方法
async def __aenter__(self):
print("進入上下文:異步打開文件")
self.file = open(self.file_path, self.mode, encoding="utf-8")
return self.file
# 異步退出方法(exc_type/val/tb 接收異常信息)
async def __aexit__(self, exc_type, exc_val, exc_tb):
print("退出上下文:異步關(guān)閉文件")
if self.file:
self.file.close()
# 調(diào)用方式不變
async def read_file():
async with AsyncOpen("test.txt", "w") as f:
f.write("手動實現(xiàn)異步上下文管理器")
asyncio.run(read_file())可以明顯看到:手動實現(xiàn)需要寫大量模板代碼,而@asynccontextmanager用一個異步生成器就搞定,代碼簡潔數(shù)倍。
總結(jié)
@asynccontextmanager是 Pythoncontextlib模塊提供的異步上下文管理器裝飾器,專為異步編程設(shè)計;- 核心是將異步生成器函數(shù)轉(zhuǎn)為異步上下文管理器,
yield前是「進入邏輯」,yield后是「退出清理邏輯」; - 必須配合
async with使用,且生成器內(nèi)要加try...finally保證資源必被清理; - 對比手動實現(xiàn)
__aenter__/__aexit__,大幅簡化異步資源管理的代碼,是異步開發(fā)中管理臨時資源(連接、文件、會話)的最佳實踐之一; - 同步場景用它的兄弟裝飾器
@contextmanager(配合with,裝飾普通生成器),異步場景用@asynccontextmanager(配合async with,裝飾異步生成器)。
到此這篇關(guān)于Python 中的異步上下文生成器 @asynccontextmanager的文章就介紹到這了,更多相關(guān)Python 異步上下文生成器 @asynccontextmanager內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python使用Pydantic進行數(shù)據(jù)驗證與序列化詳解
Pydantic庫通過使用Python類型注解來提供數(shù)據(jù)驗證和設(shè)置管理,使得數(shù)據(jù)處理變得更加可靠和高效,本文將深入探討Pydantic的使用,通過理論講解和實際代碼示例,展示如何在項目中高效利用Pydantic進行數(shù)據(jù)驗證與序列化,需要的可以了解下2025-12-12
系統(tǒng)解析Python模擬Windows文件名排序的三種實現(xiàn)方案
在文件管理場景中,Windows資源管理器的排序規(guī)則因其符合人類自然認知而備受青睞,本文將系統(tǒng)解析Windows排序規(guī)則并提供三種Python實現(xiàn)方案,大家可以根據(jù)需要進行選擇2026-03-03
Python3.7 + Yolo3實現(xiàn)識別語音播報功能
這篇文章主要介紹了Python3.7 + Yolo3識別語音播報功能,開始之前我們先得解析出來Yolo3的代碼,從而獲取到被識別出來的物體標簽,具體詳細過程跟隨小編一起看看吧2021-12-12
解決Python Matplotlib繪圖數(shù)據(jù)點位置錯亂問題
這篇文章主要介紹了解決Python Matplotlib繪圖數(shù)據(jù)點位置錯亂問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-05-05
python面向?qū)ο蟀鎸W(xué)生信息管理系統(tǒng)
這篇文章主要為大家詳細介紹了python面向?qū)ο蟀鎸W(xué)生信息管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-06-06

