Lección 35 de 45 · Python intermedio

Decoradores

Los decoradores parecen una sintaxis especial hasta que los reduces a una idea conocida: pasar una función a otra función y recibir una nueva.

FuncionesTransformación de funcionesNúcleo + ampliación
Índice · Lección 35/45
Quita primero la @ y la idea se vuelve mucho más familiar

En Python una función puede guardarse en una variable, pasarse como argumento y devolverse como resultado. Un decorador aprovecha exactamente eso para construir una versión modificada de otra función.

La sintaxis @algo es cómoda, pero no es el concepto. Primero entenderás la transformación; después usarás la abreviatura.

Evidencia de aprendizaje

Seguir la transformación función original → decorador → función envuelta sin depender de la sintaxis @.

Antes de decorar, observa una función que transforma otra función

Sin @: la transformación queda visible
def hacer_entusiasta(funcion):
    def nueva_funcion():
        return funcion() + "!"
    return nueva_funcion

def saludo():
    return "Hola"

saludo = hacer_entusiasta(saludo)
print(saludo())

hacer_entusiasta recibe la función original y devuelve nueva_funcion. A partir de la reasignación, el nombre saludo apunta a la versión envuelta.

La sintaxis @ expresa la misma transformación junto a la definición

La versión habitual
def hacer_entusiasta(funcion):
    def nueva_funcion():
        return funcion() + "!"
    return nueva_funcion

@hacer_entusiasta
def saludo():
    return "Hola"

print(saludo())

Conceptualmente, @hacer_entusiasta equivale a aplicar saludo = hacer_entusiasta(saludo) después de definir la función.

Si esto todavía cuesta: ignora temporalmente la @ y vuelve a escribir mentalmente la reasignación. Esa traducción es suficiente para seguir.

Un wrapper útil conserva la llamada y añade algo alrededor

Muchos decoradores reales registran llamadas, comprueban permisos, miden tiempo o normalizan resultados. La idea común es que el código adicional vive en un lugar y puede aplicarse a varias funciones.

No empieces intentando construir un decorador que acepte cualquier combinación de argumentos y además otro parámetro de configuración. Primero domina un wrapper pequeño. La generalización vendrá después.

Comprueba el prerrequisito: una función también es un valor

Si escribes otra = saludar sin paréntesis, no ejecutas saludar: guardas la propia función en otro nombre. Un decorador aprovecha exactamente esa capacidad para recibir una función y devolver otra.

función original→ decoradorfunción envuelta→ llamadaresultado con comportamiento extra
Equivalencia conceptual · fragmento
@entre_corchetes
def lenguaje():
    return "Python"

# equivale conceptualmente a:
lenguaje = entre_corchetes(lenguaje)

Completa el wrapper para añadir corchetes alrededor del resultado de la función original. No modifiques lenguaje().

Salida esperada
[Python]
decorador.py
La salida aparecerá aquí.

Necesito una pista
  • Dentro del wrapper ya puedes llamar a funcion().
  • Construye un string con [, el resultado y ].
Ver una solución razonada
def entre_corchetes(funcion):
    def wrapper():
        return f"[{funcion()}]"
    return wrapper

@entre_corchetes
def lenguaje():
    return "Python"

print(lenguaje())
Para después: argumentos y decoradores parametrizados

Los wrappers con *args/**kwargs y los decoradores que reciben parámetros añaden otra capa. No los necesitas para reconocer la transformación básica de una función.

Recupera lo anterior · minuto 7

Sin mirar apuntes

Quieres transformar muchos números uno a uno y aplicar después una función que envuelva el resultado. ¿Qué ideas de este bloque reconoces?

Comprobar

Un generador puede producir valores bajo demanda; una comprehension sirve si quieres materializar una lista; un decorador transforma funciones, no los datos directamente. Elige según el problema, no para usar sintaxis avanzada.

No necesitas dominar decoradores complejos para reconocer uno

  • La función original se entrega al decorador.
  • El decorador devuelve la función que se usará a partir de entonces.
  • La @ es una forma compacta de escribir esa transformación.
Señal de que la idea está asentada

si puedes reemplazar mentalmente @registro por mi_funcion = registro(mi_funcion), ya has desmontado la parte que suele parecer “mágica”.