ProgramaciónDesarrollador Backend de Python

¿Qué son los decoradores para funciones en Python, cuál es su historia y para qué se utilizan en la programación moderna?

Supere entrevistas con el asistente de IA Hintsage

Respuesta

Historia

Los decoradores aparecieron en Python como azúcar sintáctico a partir de la versión 2.4, para facilitar el trabajo con funciones de orden superior: aquellas que aceptan o devuelven otras funciones. La evolución de los enfoques para ampliar la funcionalidad de las funciones llevó a un formato conciso y expresivo: anotaciones a través de @decorador.

Problema

En proyectos grandes a menudo se necesita modificar o envolver funciones con alguna lógica adicional: registro, verificación de acceso, caché, medición del tiempo de ejecución. Sin decoradores, había que invocar manualmente las funciones envolventes, lo que hinchaba el código.

Solución

Los decoradores permiten extraer aspectos repetitivos en envolturas separadas, aumentando así la legibilidad y reutilización del código. Con @decorador se puede agregar funcionalidad a funciones y métodos de manera elegante:

Ejemplo de código:

import time def timing_decorator(func): def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) print(f'Tiempo transcurrido: {time.time() - start:.3f}s') return result return wrapper @timing_decorator def slow_function(): time.sleep(0.5) slow_function() # Mostrará cuánto tiempo tomó la ejecución

Características clave:

  • Permiten reutilizar código (principio DRY);
  • Pueden usarse para funciones, métodos, clases;
  • Permiten implementar tareas transversales (registro, caché, acceso, perfilado) de manera centralizada;

Preguntas capciosas.

¿Pueden los decoradores cambiar la firma de la función envuelta?

A menudo se considera erróneamente que la firma de la función no cambia con el decorador. En realidad, sin el uso del módulo functools.wraps, la metainformación desaparece, lo que puede provocar errores inesperados en la autodocumentación o introspección.

Ejemplo de código:

from functools import wraps def decorator(func): @wraps(func) def wrapper(*args): return func(*args) return wrapper

¿Pueden los decoradores ser parametrizados?

A menudo se responde que no es posible. En realidad, es posible crear un decorador parametrizado a través de un nivel adicional de anidación: una función que devuelve un decorador.

Ejemplo de código:

def repeat(n): def decorator(func): def wrapper(*args, **kwargs): result = None for _ in range(n): result = func(*args, **kwargs) return result return wrapper return decorator @repeat(3) def hello(): print("¡Hola!")

¿Se pueden aplicar múltiples decoradores a una función? ¿Cuál será el orden de ejecución?

A menudo se supone erróneamente que el orden no importa o que coincide con el orden en que se colocan los decoradores en el código. En realidad, se aplica primero el decorador de abajo hacia arriba, luego el siguiente y así sucesivamente.

Ejemplo de código:

def dec1(f): def wrapper(*a, **k): print("dec1") return f(*a, **k) return wrapper def dec2(f): def wrapper(*a, **k): print("dec2") return f(*a, **k) return wrapper @dec1 @dec2 def f(): print("núcleo") f() # dec1, dec2, núcleo

Errores típicos y antipatrón

  • Ignorar functools.wraps pierde la metainformación sobre la función original;
  • La lógica del decorador no considera excepciones, no se puede capturar y manejar un error desde dentro;
  • Superposición no obvia de múltiples decoradores

Ejemplo de la vida real

Caso negativo

El registro del tiempo de ejecución se añadió a 10 funciones manualmente, copiado y pegado.

Pros:

  • La lógica es clara, el código está cerca, fácil de encontrar errores.

Contras:

  • Difícil de mantener: se requerirá cambiar decenas de secciones si es necesario modificar el comportamiento.
  • El código se duplica, violando el principio DRY.

Caso positivo

Toda la lógica de temporización se extrajo a un decorador, todas las funciones donde se necesita esta métrica están envueltas con el decorador @timing_decorator.

Pros:

  • Los cambios se hacen de forma centralizada;
  • El código es más corto y legible.

Contras:

  • Puede haber pérdida de información sobre la firma sin functools.wraps, si no se tiene cuidado;
  • A los principiantes les puede resultar más difícil entender de inmediato la mecánica de trabajo de los decoradores.