0Pricing
Python Academy · Lección

Escritura de decoradores personalizados

Cree decoradores mediante funciones wrapper y functools.wraps.

Escritura de decoradores personalizados es una lección gratuita de Python Academy en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Python Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Python Academy incluye 4 lecciones en total.

Introducción

Los decoradores son funciones que envuelven otras funciones y añaden comportamiento antes o después sin modificar la función original.

¿Qué es un decorador?

@decorator sobre una función es azúcar sintáctico para: func = decorator(func). El decorador recibe la función y devuelve un wrapper.
def shout(func):
    def wrapper(*a, **kw):
        result = func(*a, **kw)
        return str(result).upper()
    return wrapper
@shout
def greet(name): return f'hello {name}'
print(greet('Alice'))

El patrón wrapper

La función wrapper tiene la misma firma que la original. Llama a la función original en su interior y añade comportamiento a su alrededor.
def timer(func):
    import time
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        print(f'{func.__name__} took {time.time()-start:.4f}s')
        return result
    return wrapper
@timer
def slow(): import time; time.sleep(0.01)
slow()

functools.wraps

@wraps(func) aplicado al wrapper conserva __name__, __doc__ y __annotations__ de la función original.
from functools import wraps
def log(func):
    @wraps(func)
    def wrapper(*a, **kw):
        print(f'calling {func.__name__}')
        return func(*a, **kw)
    return wrapper
@log
def add(a, b): return a + b
print(add.__name__)
add(1, 2)

Decorador con argumentos

Un decorador con argumentos necesita un nivel adicional de envoltura: una función factoría que recibe los argumentos y devuelve el decorador.
def repeat(n):
    def decorator(func):
        from functools import wraps
        @wraps(func)
        def wrapper(*a, **kw):
            for _ in range(n): func(*a, **kw)
        return wrapper
    return decorator
@repeat(3)
def hi(): print('hi')
hi()

Apilamiento de decoradores

Los decoradores múltiples se apilan de abajo arriba: @d2 @d1 significa func = d2(d1(func)). El más externo (el superior) se ejecuta al final.
def bold(f):
    from functools import wraps
    @wraps(f)
    def w(*a,**k): return '<b>' + f(*a,**k) + '</b>'
    return w
def italic(f):
    from functools import wraps
    @wraps(f)
    def w(*a,**k): return '<i>' + f(*a,**k) + '</i>'
    return w
@bold
@italic
def text(): return 'hello'
print(text())

Decorador basado en una clase

Una clase con __call__ puede ser un decorador. Permite conservar el estado entre llamadas.
class Counter:
    def __init__(self, func): self.func,self.count=func,0
    def __call__(self, *a, **kw):
        self.count+=1
        return self.func(*a, **kw)
@Counter
def greet(name): return f'hi {name}'
greet('Alice'); greet('Bob')
print(greet.count)

Decorador de memoización

Almacene en caché los resultados anteriores para evitar recalcularlos: if args in cache: return cache[args]. functools.lru_cache lo hace de forma integrada.
from functools import lru_cache
@lru_cache(maxsize=None)
def fib(n):
    if n < 2: return n
    return fib(n-1) + fib(n-2)
print(fib(30))

Decorador de autorización

Los decoradores son habituales para controlar el acceso: compruebe si el usuario ha iniciado sesión antes de ejecutar la función.
def requires_auth(func):
    from functools import wraps
    @wraps(func)
    def wrapper(user, *a, **kw):
        if not user.get('authenticated'):
            raise PermissionError('Not authenticated')
        return func(user, *a, **kw)
    return wrapper
@requires_auth
def dashboard(user): return 'Welcome!'
try: dashboard({'authenticated': False})
except PermissionError as e: print(e)

Decorador de reintento

Envuelva una función para reintentarlo en caso de error: for _ in range(retries): try: return func() except Exception: pass
import random
from functools import wraps
def retry(times=3):
    def dec(func):
        @wraps(func)
        def wrapper(*a, **kw):
            for i in range(times):
                try: return func(*a, **kw)
                except Exception as e:
                    if i==times-1: raise
        return wrapper
    return dec
@retry(3)
def flaky():
    if random.random()<0.7: raise ValueError
    return 'ok'
try: print(flaky())
except ValueError: print('failed')

Buenas prácticas para decoradores

Utilice siempre @functools.wraps. Mantenga los decoradores enfocados en una sola responsabilidad. Documente lo que añaden. Pruebe el decorador por separado de la función decorada.
from functools import wraps
def validate_positive(func):
    @wraps(func)
    def wrapper(x, *a, **kw):
        if x <= 0: raise ValueError(f'{x} must be positive')
        return func(x, *a, **kw)
    return wrapper
@validate_positive
def sqrt(x): return x**0.5
print(sqrt(9))

Comprobación rápida

¿Qué hace @functools.wraps(func) cuando se aplica a un wrapper?

Resumen

Decoradores: func = decorator(func). Utilice wrapper(*args, **kwargs). @wraps conserva los metadatos. Los decoradores parametrizados añaden un nivel más. Apílelos con varios @decorators.

Siga avanzando

¡Excelente progreso! Continúe para dominar el siguiente concepto.

Preguntas frecuentes

¿La lección «Escritura de decoradores personalizados» es gratis?

Sí — el texto completo de «Escritura de decoradores personalizados» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Python Academy, actualiza a CoddyKit PRO. El curso de Python Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Escritura de decoradores personalizados»?

Cree decoradores mediante funciones wrapper y functools.wraps. Practicas Python Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Python Academy?

No se requiere experiencia previa. Python Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.

¿Cuánto tiempo toma la lección «Escritura de decoradores personalizados»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Python Academy?

Sí. Cada lección de Python Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Funciones como objetos de primera clase
  2. Escritura de decoradores personalizados
  3. El decorador @property
  4. @classmethod y @staticmethod
← Volver a Python Academy