Decoradores en Python
Este artículo explica los decoradores en Python.
YouTube Video
Decoradores en Python
Los decoradores en Python son una característica poderosa utilizada para añadir funcionalidad adicional a las funciones o métodos. Los decoradores te permiten añadir nueva funcionalidad sin modificar el código existente, mejorando así la reutilización y el mantenimiento del código.
Conceptos básicos de los decoradores
Los decoradores en Python funcionan tomando una función como argumento y devolviendo una nueva función con funcionalidad añadida. Con los decoradores, puedes añadir fácilmente preprocesamiento o posprocesamiento a las funciones.
Sin decoradores, alterar el comportamiento de una función requiere editar directamente la función en sí misma. Con los decoradores, puedes extender la funcionalidad sin modificar la implementación original.
Estructura básica de un decorador
1def my_decorator(func):
2 def wrapper():
3 print("Before the function call")
4 func()
5 print("After the function call")
6 return wrapper
7
8@my_decorator
9def say_hello():
10 print("Hello!")
11
12say_hello()
En este ejemplo, la función my_decorator
actúa como un decorador, envolviendo la función say_hello
. El decorador se aplica utilizando la sintaxis @my_decorator
, y cuando se llama a say_hello()
, el preprocesamiento y posprocesamiento proporcionados por el decorador se ejecutan automáticamente.
Cómo funcionan los decoradores
Los decoradores funcionan en los siguientes pasos.
-
El decorador toma una función (o método) como argumento.
-
Define una función envoltura para ejecutar la función original.
-
La función envoltura realiza un procesamiento adicional antes o después de ejecutar la función original.
-
El decorador devuelve la función envoltura. Como resultado, la función original se reemplaza por una nueva función decorada.
Decoradores para funciones con argumentos
Al aplicar un decorador a una función con argumentos, la función envoltura debe ajustarse para aceptar esos argumentos.
1def my_decorator(func):
2 def wrapper(*args, **kwargs):
3 print("Before the function call")
4 result = func(*args, **kwargs)
5 print("After the function call")
6 return result
7 return wrapper
8
9@my_decorator
10def greet(name):
11 print(f"Hello, {name}!")
12
13greet("Alice")
Al usar *args
y **kwargs
, es posible manejar funciones que aceptan cualquier número de argumentos y argumentos nombrados. Esto permite que los decoradores se apliquen de forma genérica a funciones con cualquier tipo de argumentos.
Ejemplos de Aplicación de Decoradores
Decorador para Registro de Logs
Los decoradores se usan a menudo para añadir funcionalidad de registro de logs. Por ejemplo, al crear un decorador que registre antes y después de ejecutar una función, puedes registrar cuándo se llama a la función y cuánto tiempo tarda en ejecutarse.
1import time
2
3def log_time(func):
4 def wrapper(*args, **kwargs):
5 start_time = time.time()
6 print(f"Calling function '{func.__name__}'...")
7 result = func(*args, **kwargs)
8 end_time = time.time()
9 print(f"Function '{func.__name__}' completed in {end_time - start_time} seconds")
10 return result
11 return wrapper
12
13@log_time
14def long_task(duration):
15 time.sleep(duration)
16 print("Task completed!")
17
18long_task(2)
En este ejemplo, el decorador log_time
mide el tiempo de ejecución de una función y lo muestra como un log. La función long_task
está envuelta con el decorador, y su tiempo de ejecución se registra durante la ejecución.
Decorador para Gestión de Permisos
Los decoradores también pueden ser utilizados para la gestión de permisos. Por ejemplo, puedes restringir el procesamiento verificando si un usuario tiene permisos específicos.
1def requires_permission(user_role):
2 def decorator(func):
3 def wrapper(*args, **kwargs):
4 if user_role != "admin":
5 print("Permission denied!")
6 return
7 return func(*args, **kwargs)
8 return wrapper
9 return decorator
10
11@requires_permission("admin")
12def delete_user(user_id):
13 print(f"User {user_id} has been deleted.")
14
15delete_user(123) # Executed
16delete_user = requires_permission("guest")(delete_user)
17delete_user(456) # Permission denied!
El decorador requires_permission
restringe la ejecución de una función según el rol del usuario. Al consolidar dicha lógica en un decorador, puedes evitar que la lógica de gestión de permisos esté dispersa en tu código, mejorando la legibilidad.
Decoradores Anidados
Es posible aplicar múltiples decoradores. Cuando se aplican múltiples decoradores a una función, se ejecutan en orden de arriba hacia abajo.
1def uppercase(func):
2 def wrapper(*args, **kwargs):
3 result = func(*args, **kwargs)
4 return result.upper()
5 return wrapper
6
7def exclaim(func):
8 def wrapper(*args, **kwargs):
9 result = func(*args, **kwargs)
10 return result + "!"
11 return wrapper
12
13@uppercase
14@exclaim
15def greet(name):
16 return f"Hello, {name}"
17
18print(greet("Alice")) # "HELLO, ALICE!"
En este ejemplo, se aplican dos decoradores a la función greet
. El decorador @exclaim
añade un signo de exclamación al final de la cadena, y luego @uppercase
convierte la cadena a mayúsculas.
Decoradores para Métodos de Clase
Los decoradores también pueden ser utilizados con métodos de clase. Esto es particularmente útil cuando quieres controlar el comportamiento de los métodos dentro de una clase. Al aplicar decoradores a métodos de clase, necesitas tener en cuenta argumentos como self
o cls
.
1def log_method_call(func):
2 def wrapper(self, *args, **kwargs):
3 print(f"Calling method '{func.__name__}'...")
4 return func(self, *args, **kwargs)
5 return wrapper
6
7class MyClass:
8 @log_method_call
9 def greet(self, name):
10 print(f"Hello, {name}")
11
12obj = MyClass()
13obj.greet("Bob")
En este ejemplo, el decorador log_method_call
se aplica al método greet
para mostrar un log cuando se llama al método.
Precauciones con los Decoradores
Existen algunas consideraciones al usar decoradores. Los decoradores pueden cambiar potencialmente el nombre y la cadena de documentación de la función original (p. ej., __name__
o __doc__
), por lo que se recomienda usar functools.wraps
para preservar esta información.
1import functools
2
3def my_decorator(func):
4 @functools.wraps(func)
5 def wrapper(*args, **kwargs):
6 print("Before the function call")
7 return func(*args, **kwargs)
8 return wrapper
El uso de @functools.wraps
asegura que los metadatos de la función original se transfieran correctamente a la función contenedora.
Resumen
Los decoradores en Python son una herramienta muy poderosa que te permite añadir funcionalidad adicional a funciones y métodos de manera concisa. Se pueden usar en varios escenarios para reducir la duplicación de código y mejorar la mantenibilidad y reutilización. Al comprender cómo funcionan los decoradores, puedes escribir código más eficiente y flexible.
Puedes seguir el artículo anterior utilizando Visual Studio Code en nuestro canal de YouTube. Por favor, también revisa nuestro canal de YouTube.