Curso de Python / Comprender decoradores y gestores de contexto

Un decorador envuelve el comportamiento de una llamada a función; un gestor de contexto controla la preparación y limpieza de un bloque. Ambos permiten extraer infraestructura repetida, siempre que se mantengan deliberadamente los contratos de retorno, argumentos y excepciones.

Separar envoltura y duración de recursos

@decorator aplica una función decoradora a la función definida y asigna su resultado al nombre de esta. Es código ejecutable, no un comentario. En cambio, with organiza la entrada y salida de un contexto y puede asegurar limpieza cuando el cuerpo genera una excepción.

functools.wraps conserva metadatos como el nombre y la documentación de la función original. No demuestra que el envoltorio preserve automáticamente todas las garantías de tipos, seguridad o gestión de errores. Esa responsabilidad sigue siendo del código que escribes.

Ejecutar un envoltorio y una colección temporal

Guarda el siguiente programa completo como wrappers.py y ejecútalo con python wrappers.py. functools y contextlib forman parte de la biblioteca estándar.

from functools import wraps
from contextlib import contextmanager

def announce(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("start")
        return func(*args, **kwargs)
    return wrapper

@contextmanager
def temporary_items():
    items = []
    try:
        yield items
    finally:
        items.clear()
        print("cleared")

@announce
def total(amounts):
    return sum(amounts)

with temporary_items() as items:
    items.extend([3500, 8000])
    print(total(items))
print(total.__name__)
print(items)
start
11500
cleared
total
[]

La llamada decorada anuncia start y conserva el retorno de la suma. Al salir del bloque, el contexto vacía la lista e imprime cleared. Gracias a wraps, el nombre consultado sigue siendo total. La referencia items continúa existiendo, pero su lista ya está vacía.

Seguir retornos y excepciones entre capas

En términos de transformación, decorar total con announce equivale a asignar total = announce(total) después de definirla. El envoltorio recibe *args y **kwargs y reenvía los argumentos. Si omite return, cambia el resultado visible a None, aunque la función interna calcule correctamente.

Una función decorada con contextmanager cede el control una vez mediante yield. Al terminar el bloque, vuelve a ejecutarse para completar la salida. La limpieza está en finally para que ocurra también ante un fallo. Capturar una excepción alrededor de yield sin volver a lanzarla puede suprimirla; decide explícitamente si eso representa una recuperación válida.

La referencia de contextmanager detalla ese flujo, y la referencia de wraps explica los metadatos conservados. Los fundamentos están en las excepciones y finally y los generadores.

Comprobar limpieza y elegir la abstracción

  1. Lanza un ValueError deliberado dentro del with. Captúralo fuera y verifica que se ejecuta la limpieza.
  2. Inspecciona la lista después de salir para confirmar que está vacía.
  3. Retira temporalmente el return del envoltorio, compara el resultado y restáuralo.

No confundas limpieza con reintento. Un decorador que repite una petición con efectos puede duplicar transacciones. Su política de intentos, demoras e idempotencia necesita otro diseño. Para una operación pequeña que solo aparece una vez, un bloque directo puede resultar más fácil de comprender que introducir una nueva abstracción.