Python?中的異步上下文生成器?@asynccontextmanager詳解
Python 中的 @asynccontextmanager 裝飾器,它是異步編程中用于快速定義異步上下文管理器的工具,基于 contextlib 模塊實現(xiàn),核心作用是讓你用異步生成器(async yield) 替代手動定義異步上下文管理器的兩個魔法方法,大幅簡化代碼。
先鋪墊:異步上下文管理器的基礎(chǔ)
在異步代碼中,我們會用 async with 語句管理資源(比如異步連接數(shù)據(jù)庫、異步打開文件、異步網(wǎng)絡會話),而能被 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之前的代碼 → 對應__aenter__()的邏輯(進入上下文,初始化/獲取資源); - 生成器中
yield之后的代碼 → 對應__aexit__()的邏輯(退出上下文,釋放/清理資源); yield后面的值 → 就是async with ... as 變量中「變量」接收到的資源對象。
基本使用語法
from contextlib import asynccontextmanager
import asyncio
# 用@asynccontextmanager裝飾異步生成器函數(shù)
@asynccontextmanager
async def 異步上下文管理器名(參數(shù)):
# 1. 進入邏輯:初始化/獲取異步資源(對應__aenter__)
資源 = 等待異步操作獲取資源()
try:
yield 資源 # 把資源返回給async with ... as 變量,暫停執(zhí)行
finally:
# 2. 退出邏輯:釋放/清理資源(對應__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)絡請求等場景,比如模擬異步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è)務代碼使用
finally:
# 2. 退出邏輯:關(guān)閉連接,釋放資源
await conn.close()
# 業(yè)務代碼:使用數(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實現(xiàn)的檢測web服務器健康狀況的小程序
這篇文章主要介紹了Python實現(xiàn)的檢測web服務器健康狀況的小程序,本文使用socket庫來實現(xiàn),需要的朋友可以參考下2014-09-09
pytorch 權(quán)重weight 與 梯度grad 可視化操作
這篇文章主要介紹了pytorch 權(quán)重weight 與 梯度grad 可視化操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06
Python使用嵌套循環(huán)實現(xiàn)圖像處理算法
這篇文章主要給大家詳細介紹Python如何使用嵌套循環(huán)實現(xiàn)圖像處理算法,文中有詳細的代碼示例,具有一定的參考價值,需要的朋友可以參考下2023-07-07
python向已存在的excel中新增表,不覆蓋原數(shù)據(jù)的實例
下面小編就為大家分享一篇python向已存在的excel中新增表,不覆蓋原數(shù)據(jù)的實例,具有很好超參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-05-05
詳解如何使用SQLAlchemy連接數(shù)據(jù)庫
這篇文章主要為大家詳細介紹了如何使用 SQLAlchemy 連接數(shù)據(jù)庫、建立模型、操作表、以及查詢操作表數(shù)據(jù)等內(nèi)容,感興趣的小伙伴可以跟隨小編一起學習一下2023-11-11
python正則表達式去除兩個特殊字符間的內(nèi)容方法
今天小編就為大家分享一篇python正則表達式去除兩個特殊字符間的內(nèi)容方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-12-12

