Gestores y Iteradores de Contexto Asíncronos
async with entra y sale de configuraciones y limpiezas que se pueden esperar. async for consume iteradores asíncronos que hacen await entre elementos, lo cual es esencial para el streaming de cuerpos HTTP, cursores de bases de datos y tramas de websocket.
Receta
from contextlib import asynccontextmanager
@asynccontextmanager
async def session():
print("abrir")
try:
yield {"ok": True}
finally:
print("cerrar")Cuándo usar esto:
- Ámbitos de conexión/sesión de base de datos
- Respuestas de streaming de
httpx - Bucles de mensajes de Websocket
- Bloqueos distribuidos con adquisición asíncrona
- Flujos asíncronos similares a archivos (aiofiles)
Ejemplo de Trabajo
import asyncio
from contextlib import asynccontextmanager
@asynccontextmanager
async def managed_connection(name: str):
print("conectar", name)
try:
yield name
finally:
print("desconectar", name)
class Countdown:
def __init__(self, start: int) -> None:
self._n = start
def __aiter__(self):
return self
async def __anext__(self) -> int:
if self._n <= 0:
raise StopAsyncIteration
await asyncio.sleep(0.01)
self._n -= 1
return self._n + 1
async def main() -> None:
async with managed_connection("db") as conn:
print("usando", conn)
async for value in Countdown(3):
print("tick", value)
asyncio.run(main())Lo que esto demuestra:
@asynccontextmanagerreflejacontextmanagersíncrono conawaitasync withllama a los awaitables__aenter__/__aexit____aiter__/__anext__potencianasync forStopAsyncIterationfinaliza la iteración asíncrona
Inmersión Profunda
Resumen de Protocolos
| Protocolo | Métodos |
|---|---|
| Gestor de contexto asíncrono | __aenter__, __aexit__ |
| Iterador asíncrono | __aiter__, __anext__ |
| Generador asíncrono | async def con yield |
Patrón de Streaming
async with client.stream("GET", url) as resp:
async for chunk in resp.aiter_bytes():
process(chunk)Trampas Comunes
- Usar
withsíncrono en un gestor asíncrono - no espera la limpieza. Solución: siempre usarasync with. - Bloqueo en
__aexit__- retrasa la cancelación. Solución: esperar rutas de cierre no bloqueantes. - Generador asíncrono no cerrado -
aclose()no se llama enbreak. Solución: patrónasync with async_generator()otry/finally. - Mezclar
forsíncrono en un iterador asíncrono -TypeError. Solución: soloasync for. async forinfinito sin cancelación - la tarea nunca termina. Solución: tiempo de espera, evento de desconexión, cancelación deTaskGroup.
Alternativas
| Alternativa | Usar cuándo | No usar cuándo |
|---|---|---|
try/finally await manual | Scripts pequeños | Patrón de recurso reutilizable |
Iterador síncrono + to_thread | Flujo síncrono heredado | Existe un flujo asíncrono nativo |
| Streaming con callbacks | SDK antiguo | Cliente asíncrono moderno disponible |
Preguntas Frecuentes
¿`asynccontextmanager` vs clase?
Decorador para yield/finally simples; clase cuando hay estado complejo en la entrada/salida.
¿Puede `__aexit__` suprimir errores?
Devolver True para suprimir - misma semántica que los gestores síncronos.
¿`async for` vs `while receive`?
async for es idiomático en protocolos; await recv() manual está bien para sockets con lógica personalizada.
¿Salir de `async for`?
break activa aclose() en generadores asíncronos - verificar que el generador maneje GeneratorExit.
¿Múltiples `async with`?
async with A(), B(): o AsyncExitStack de contextlib (soporte asíncrono).
¿Patrón `aiofiles`?
async with aiofiles.open(...) as f: await f.read() - E/S de archivos asíncrona.
¿Sesión asíncrona de SQLAlchemy?
async with session.begin(): ámbito de transacción - ver la página de bases de datos asíncronas.
¿Probar iteradores asíncronos?
Controlar __anext__ hasta StopAsyncIteration o recopilar mediante async for en pytest-asyncio.
¿Limpieza de generador asíncrono?
Usar try/finally dentro del generador; los consumidores deben usar async with aclosing(gen()) (3.10+).
¿Bucle de websocket?
async for message in ws: hasta que la conexión se cierre - manejar ConnectionClosed con gracia.
Relacionado
- Context Managers as a Pattern - versión síncrona
- Async HTTP with httpx/aiohttp - cuerpos de streaming
- Async Databases - ámbitos de sesión
- Asyncio Basics - introducción a
await
Versiones de la pila: Esta página fue escrita para Python 3.14.0 (estable 3.14, mantenimiento 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, y uv 0.6+.