6.1 Decoradores para métodos de clase
Los decoradores también pueden utilizarse para métodos de clase. Es importante recordar que para los métodos de clase es necesario pasar correctamente los argumentos self o cls.
Aún no hemos cubierto en detalle el funcionamiento de las clases, pero me gustaría que supieras que existe tal posibilidad para los decoradores.
def log_method_call(func):
def wrapper(self, *args, **kwargs):
print(f"Llamada al método {func.__name__}")
return func(self, *args, **kwargs)
return wrapper
class MyClass:
@log_method_call
def say_hello(self):
print("Hello from MyClass!")
obj = MyClass()
obj.say_hello()
Explicación
Decorador (log_method_call): Este decorador toma el método func y devuelve una nueva función wrapper que imprime un mensaje antes de la llamada al método.
Método de clase con decorador (say_hello): El método say_hello está envuelto con el decorador log_method_call, lo que añade un comportamiento adicional al ser llamado.
Salida:
Llamada al método say_hello
Hello from MyClass!
6.2 Varios decoradores
Puedes usar varios decoradores para una función, aplicándolos en capas uno sobre otro. Los decoradores se aplican en el orden inverso a su declaración.
def decorator1(func):
def wrapper():
print("Decorador 1")
func()
return wrapper
def decorator2(func):
def wrapper():
print("Decorador 2")
func()
return wrapper
@decorator1
@decorator2
def say_hello():
print("Hello!")
say_hello()
Explicación
Decoradores (decorator1 y decorator2): Estos decoradores añaden sus mensajes antes de la llamada a la función func.
Función con decoradores (say_hello): La función say_hello está envuelta con ambos decoradores. Primero se aplica decorator2, luego decorator1.
Salida:
# Decorador 1
# Decorador 2
Hello!
6.3 Decoradores integrados
Python proporciona varios decoradores integrados para tareas estándar, como métodos estáticos, métodos de clase y propiedades.
@staticmethod
El decorador @staticmethod se utiliza para crear un método estático, que no requiere una instancia de la clase para ser llamado.
class MyClass:
@staticmethod
def static_method():
print("Este es un método estático.")
MyClass.static_method()
@classmethod
El decorador @classmethod se utiliza para crear un método de clase, que toma la clase (en lugar de la instancia) como primer argumento.
class MyClass:
@classmethod
def class_method(cls):
print(f"Este es un método de clase {cls.__name__}.")
MyClass.class_method()
@property
El decorador @property se utiliza para crear getters, setters y deleters para atributos.
class MyClass:
def __init__(self, value):
self.hidden_value = value
@property
def value(self):
return self.hidden_value
@value.setter
def value(self, new_value):
self.hidden_value = new_value
obj = MyClass(10)
print(obj.value) # Salida: 10
obj.value = 20
print(obj.value) # Salida: 20
Estos son decoradores integrados y su funcionamiento correcto es gestionado por el propio intérprete de Python.
6.4 Ejemplos de uso de decoradores
Logging
Los decoradores pueden ser utilizados para hacer logging de las llamadas a funciones y métodos.
def log_call(func):
def wrapper(*args, **kwargs):
print(f"Llamada a la función {func.__name__} con argumentos {args} y {kwargs}")
return func(*args, **kwargs)
return wrapper
@log_call
def add(x, y):
return x + y
print(add(2, 3))
Control de acceso
Los decoradores pueden ser utilizados para el control de acceso a funciones y métodos.
def require_authentication(func):
def wrapper(*args, **kwargs):
if not args[0].is_authenticated:
raise PermissionError("Usuario no autenticado.")
return func(*args, **kwargs)
return wrapper
class User:
def __init__(self, is_authenticated):
self.is_authenticated = is_authenticated
@require_authentication
def view_profile(self):
print("Perfil del usuario")
user = User(is_authenticated=True)
user.view_profile() # Llamada exitosa
user2 = User(is_authenticated=False)
user2.view_profile() # PermissionError: Usuario no autenticado.
Cacheo
Los decoradores pueden ser utilizados para cachear los resultados de una función.
def cache(func):
cached_results = {}
def wrapper(*args):
if args in cached_results:
return cached_results[args]
result = func(*args)
cached_results[args] = result
return result
return wrapper
@cache
def fib(n):
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
print(fib(35))
GO TO FULL VERSION