Cuando trabajas en un entorno profesional con Python, es muy común necesitar añadir comportamientos antes o después de una función sin alterar su lógica interna. Los decoradores son la herramienta que permite lograr exactamente eso: extender la funcionalidad de una función manteniéndola intacta por dentro. A continuación se explica cómo funcionan, cuál es su estructura y cómo aplicarlos en casos prácticos.
¿Qué es un decorador y cuál es su estructura?
Un decorador en Python es una función que recibe otra función como parámetro, le añade comportamiento adicional y retorna una nueva función modificada. La sintaxis se apoya en el uso del símbolo @ seguido del nombre del decorador, colocado justo encima de la función que se desea extender [0:28].
La estructura general sigue este patrón:
- Se define una función decoradora que recibe como argumento la función original.
- Dentro de ella se crea una función interna, comúnmente llamada wrapper (envolvedor), que contiene el nuevo comportamiento.
- Se ejecuta la función original dentro del wrapper.
- El decorador retorna la función wrapper.
Este concepto de wrapper es fundamental: su rol es envolver la función original con las instrucciones adicionales que necesites, ya sea registrar logs, validar permisos o cualquier otra lógica.
¿Cómo implementar un decorador paso a paso?
El primer ejemplo práctico consiste en crear un decorador que registre un log antes y después de procesar un pago [2:06]. La función base es muy sencilla:
python
@registrar_log
def procesar_pago():
print("Procesando pago...")
Y el decorador se construye así:
python
def registrar_log(funcion):
def wrapper():
print("Log de la transacción iniciando...")
funcion()
print("Log terminado...")
return wrapper
Al llamar procesar_pago(), el resultado sigue tres pasos en orden:
- Se imprime "Log de la transacción iniciando...".
- Se ejecuta "Procesando pago...".
- Se imprime "Log terminado...".
Fíjate que la función procesar_pago no fue modificada internamente. Todo el comportamiento extra vive en el decorador, lo que permite reutilizarlo en otras funciones.
¿Cómo usar decoradores con parámetros en la función original?
El segundo ejemplo añade complejidad real: un decorador que verifica si un empleado tiene rol de administrador antes de permitirle eliminar a otro empleado [5:15]. La función base recibe un diccionario con información del empleado:
python
@verificar_rol
def eliminar_empleado(empleado):
print(f"El empleado {empleado['nombre']} ha sido eliminado")
El decorador valida el rol antes de ejecutar la acción:
python
def verificar_rol(funcion):
def wrapper(empleado):
if empleado["rol"] == "admin":
return funcion(empleado)
else:
print("Acceso denegado. Solo los administradores pueden acceder.")
return wrapper
Cuando el wrapper recibe el parámetro empleado, comprueba la clave rol del diccionario. Si el valor es "admin", se ejecuta la función original. En caso contrario, se deniega el acceso con un mensaje claro.
¿Qué sucede al ejecutar con distintos roles?
Se definen dos diccionarios de prueba [7:42]:
python
admin = {"nombre": "Carlos", "rol": "admin"}
empleado = {"nombre": "Ana", "rol": "empleado"}
Al llamar eliminar_empleado(admin), el resultado es: "El empleado Carlos ha sido eliminado". Al llamar eliminar_empleado(empleado), se obtiene: "Acceso denegado. Solo los administradores pueden acceder" [9:12].
Un detalle importante que surgió durante la implementación fue un error de inconsistencia en los nombres de las llaves del diccionario. La clave definida como "rol" debe coincidir exactamente con la que se consulta en el decorador. Este tipo de bug es frecuente cuando se mezclan idiomas en las claves.
¿Por qué los decoradores son tan útiles en proyectos reales?
Los decoradores permiten separar responsabilidades de forma limpia. En lugar de mezclar validaciones, logs o métricas dentro de cada función, se encapsulan en decoradores reutilizables. Esto significa que puedes:
- Añadir registro de actividad a cualquier función con
@registrar_log.
- Controlar acceso basado en roles con
@verificar_rol.
- Decidir cuándo ejecutar o bloquear una función sin tocar su código.
El reto propuesto consiste en crear un decorador que registre cada acción de un empleado en un archivo de texto, combinando lo aprendido sobre decoradores con manejo de archivos.
¿Qué otros decoradores se te ocurren para este sistema de gestión de empleados? Comparte tus ideas y soluciones en los comentarios.