@asynccontextmanager는 Python의 contextlib 모듈에서 제공하는 데코레이터로, 비동기 컨텍스트 관리자를 간단하게 만들 수 있게 해준다.
이를 이용하면 async with문과 함께 사용할 수 있는 컨텍스트 관리자를 쉽게 구현할 수 있다.
자원의 획득과 해제를 책임지는 객체
예시
# 동기
with open("file.txt") as f:
data = f.read()
# 비동기
async with aiofiles.open("file.txt", "r") as f:
data = await f.read()
사용하면 좋을 경우
자원 획득과 정리를 단순하고 간결하게 처리하고 싶을 때
복잡한 상태 관리가 필요하지 않은 짧은 생명주기의 비동기 자원을 다룰 때
| 항목 | async def | 비동기 컨텍스트 매니저 |
|---|---|---|
| 목적 | 비동기 함수 실행 | 자원 열고 닫기 |
| 사용 문법 | await func() | async with manager: |
| 핵심 메서드 | 없음 | __aenter__(), __aexit__() |
| 상태 관리 | 없음 | 있음 (초기화/정리 구조) |
여기서 aenter()와 aexit()란?
비동기 컨텍스트 매니저는 내부적으로 다음 메서드를 사용
__aenter__() : async with 블록 진입 시 실행__aexit__() : 블록 종료 시 실행 (예외 발생 여부와 관계없이)직접 클래스로 구현할 경우, 이 메서드를 정의해야한다.
from asyncio import run
from contextlib import AsyncContextDecorator
class mycontext(AsyncContextDecorator):
async def __aenter__(self):
print('Starting')
return self
async def __aexit__(self, *exc):
print('Finishing')
return False
사용 방법
@context()
async def function():
print('The bit in the middle')
>>> run(function(())
The bit in the middle
Finishing
@context()
async def function():
async with mycontext():
print('The bit in the middle')
>>> run(function())
Starting
The bit in the middle
Finishing
@asynccontextmager 란?python의 contextlib 모듈에서 제공하는 데코레이터이다.
비동기 컨텍스트 매니저를 함수 하나로 쉽게 작성할 수 있게 도와준다.
1. DB 연결 관리 예시
@asynccontextmanager
async def get_db():
db = await connect()
try:
yield db
finally:
await db.close()
2. python 공식 문서 예시
# Added in version 3.7.
from contextlib import asynccontextmanager
@asynccontextmanager
async def get_connection():
conn = await acquire_db_connection()
try:
yield conn
finally:
await release_db_connection(conn)
async def get_all_users():
async with get_connection() as conn:
return conn.query('SELECT ...')
get_connection()함수는 async with 문에서 사용할 수 있는 컨텍스트 매니저처럼 작동yield는 자원을 블록으로 넘겨주는 지점이고, finally는 자원 해제 시 실행Python 3.10부터 데코레이터로 사용 가능
import time
from contextlib import asynccontextmanager
@asynccontextmanager
async def timeit():
now = time.monotonic()
try:
yield
finally:
print(f'it took {time.monotonic() - now}s to run')
@timeit()
async def main():
# ... async code ...
@timeit()은 main() 함수 실행 전후로 실행 시간을 측정| 방법 | 구현 필요 | 장점 |
|---|---|---|
| 클래스 직접 구현 | __aenter__() / __aexit__() | 더 많은 제어 |
@asynccontextmanager | 구현 필요 없음 (자동 처리) | 간결하고 직관적 |
yield는 한 번만 사용할 수 있음yield가 함수 내부에 반드시 1회 존재해야 함yield 전 → 초기화, 이후 → 정리 작업try...finally로 정리 코드 작성할 것async def: 단순 비동기 함수async with: 자원 관리@asynccontextmanager: 복잡한 컨텍스트 매니저를 간단하게 구현