Una guía sobre decoradores de Python para programadores avanzados
Los decoradores de Python son una herramienta potente y flexible para modificar el comportamiento de funciones o métodos. Permiten a los programadores ampliar o alterar la funcionalidad de objetos invocables de una manera clara, legible y fácil de mantener. Este artículo explora conceptos avanzados relacionados con los decoradores de Python, incluidos los decoradores anidados, los argumentos de los decoradores y los decoradores basados en clases.
¿Qué son los decoradores?
Los decoradores son funciones que modifican el comportamiento de otra función. Envuelven otra función para extender su comportamiento sin modificar explícitamente su código. Los decoradores se definen utilizando la sintaxis @decorator_name
y se colocan encima de la definición de la función.
Sintaxis básica del decorador
Un decorador simple toma una función como argumento, define una función interna que agrega algún comportamiento y luego devuelve la función interna.
def my_decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
Funciones decoradoras con argumentos
Los decoradores pueden ser más flexibles si aceptan argumentos. Para crear un decorador de este tipo, es necesario escribir una función que devuelva un decorador. Esto permite agregar un comportamiento más dinámico a los decoradores.
def repeat(num_times):
def decorator_repeat(func):
def wrapper(*args, **kwargs):
for _ in range(num_times):
func(*args, **kwargs)
return wrapper
return decorator_repeat
@repeat(num_times=3)
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
Decoradores de nidos
Los decoradores se pueden anidar para combinar múltiples comportamientos. Por ejemplo, podemos usar dos o más decoradores en una sola función.
def uppercase_decorator(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result.upper()
return wrapper
def repeat_decorator(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result + result
return wrapper
@repeat_decorator
@uppercase_decorator
def say_word(word):
return word
print(say_word("hello"))
Decoradores basados en clases
En Python, los decoradores también se pueden implementar como clases mediante el método __call__
. Los decoradores basados en clases son útiles cuando se necesita una gestión y un comportamiento de estados más complejos.
class CountCalls:
def __init__(self, func):
self.func = func
self.num_calls = 0
def __call__(self, *args, **kwargs):
self.num_calls += 1
print(f"Call {self.num_calls} of {self.func.__name__!r}")
return self.func(*args, **kwargs)
@CountCalls
def say_hello():
print("Hello!")
say_hello()
say_hello()
Uso de functools.wraps
para conservar metadatos
Cuando escribes decoradores, la función decorada pierde sus metadatos originales, como su nombre y su cadena de documentación. El decorador functools.wraps
se puede utilizar para copiar los metadatos de la función original a la función contenedora.
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("Wrapper function executed before", func.__name__)
return func(*args, **kwargs)
return wrapper
@my_decorator
def display_info(name, age):
"""Displays name and age."""
print(f"display_info ran with arguments ({name}, {age})")
print(display_info.__name__) # Output: display_info
print(display_info.__doc__) # Output: Displays name and age.
Conclusión
Los decoradores de Python son una característica poderosa que permite un diseño flexible de código y la modificación del comportamiento. El uso avanzado, como los decoradores anidados, los decoradores con argumentos y los decoradores basados en clases, puede brindar aún más funcionalidad y legibilidad a los programas de Python. Al comprender y utilizar los decoradores correctamente, los desarrolladores pueden escribir código más conciso, eficiente y legible.