Decoradores
Los decoradores son una forma de modificar o extender la funcionalidad de una función en Python. Básicamente, un decorador es una función que toma otra función y la modifica sin cambiar el código fuente original.
Estos se utilizan para agregar funcionalidad a una función existente sin tener que modificar su código fuente original. Esto puede ser útil para agregar características como verificación de entrada, registro de llamadas o temporización a una función sin tener que agregar esta funcionalidad a la función en sí.
¿Cómo se crean los decoradores?
Para crear un decorador en Python, simplemente definimos una función que tome otra función como argumento y devuelva una función modificada. Por ejemplo, si queremos crear un decorador que imprima un mensaje antes y después de la ejecución de una función, podemos hacerlo de la siguiente manera:
def decorator(func):
def wrapper(*args, **kwargs):
# código que se ejecuta antes de la función original
result = func(*args, **kwargs)
# código que se ejecuta después de la función original
return result
return wrapper
En este ejemplo, el decorador "decorator" toma una función "func" como argumento. La función "wrapper" se define dentro del decorador y toma cualquier número de argumentos posicionales y de palabras clave. La función "wrapper" ejecuta algún código antes y después de llamar a la función original "func". La función "wrapper" devuelve el resultado de la función original.
Para aplicar este decorador a una función, simplemente coloque el símbolo "@" antes del nombre del decorador sobre la definición de la función:
@decorator
def my_function():
# código de la función original
Esta es la manera habitual en la que se aplica un decorador, sin embargo lo que esta aciendo internamente es asignar la funcion wraper()
devuelta por el decorador decorator()
a nuestra funcion my_function()
. El sigueinte codigo hace los mimso que el mostrado anteriormente:
my_function = decorator(my_function)
¿Que es el wrapper?
El wrapper
es una función que se utiliza en la creación de decoradores. Su función principal es envolver la función original, que se quiere decorar, dentro de otra función que agrega alguna funcionalidad adicional sin modificar la función original.
El wrapper
es una función interna que se define dentro de la función decoradora y que tiene acceso a la función original y a los argumentos que se le pasan. Es decir, el wrapper
es la función que se ejecuta antes y después de la función original, y puede hacer cualquier cosa que se necesite antes o después de la llamada a la función original
Por ejemplo, si se tiene el siguiente decorador que agrega una función adicional de impresión antes de la llamada a la función original:
my_decorator(func):
def wrapper(*args, **kwargs):
print("Calling the function")
return func(*args, **kwargs)
return wrapper
En este caso, wrapper
es la función interna que envuelve a la función original func
. *args
y **kwargs
permiten que el decorador acepte cualquier número de argumentos y parámetros, lo que es útil en caso de que se quiera aplicar el decorador a una función con diferentes números de argumentos y parámetros.
En la última línea del decorador, la función wrapper
se retorna para que se pueda hacer la reasginacion de la funcion de la que se ha hablado anteriormente
Decoradores con argumentos
Es importante tener en cuenta que los decoradores también pueden recibir argumentos. Para ello, debemos definir una función que devuelva el decorador deseado. Por ejemplo, si queremos un decorador que acepte un argumento entero y añada ese número de saltos de línea después de la ejecución de la función original, podemos definirlo de la siguiente manera:
def newline_decorator(num_lines):
def actual_decorator(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
print("\n" * num_lines)
return result
return wrapper
return actual_decorator
Para utilizar este decorador con argumentos, simplemente debemos llamar a la función que devuelve el decorador con los argumentos deseados y colocar el resultado encima de la definición de la función que queremos decorar.
@newline_decorator(3)
def my_function():
print("Hello, world!")
En este caso, la función my_function()
será decorada con un salto de línea de tres líneas después de su ejecución.
>>> my_function()
Hello, world!
Ejemplos de decoradores en Python
Decorador de temporización
Un decorador de temporización se puede utilizar para medir el tiempo de ejecución de una función. Por ejemplo, si queremos medir el tiempo que tarda una función en ejecutarse, podemos usar el siguiente decorador:
import time
def temporizador(func):
def wrapper(*args, **kwargs):
inicio = time.time()
resultado = func(*args, **kwargs)
fin = time.time()
print(f"La función {func.__name__} tardó {fin - inicio} segundos en ejecutarse.")
return resultado
return wrapper
Para aplicar este decorador a una función, podemos usar la sintaxis @temporizador antes de la definición de la función:
@temporizador
def mi_funcion():
# Código de la función
Decorador de registro de llamadas
Un decorador de registro de llamadas se puede utilizar para registrar todas las llamadas a una función. Por ejemplo, si queremos registrar todas las llamadas a una función y escribir las llamadas en un archivo de registro, podemos usar el siguiente decorador:
def registro_de_llamadas(func):
def wrapper(*args, **kwargs):
resultado = func(*args, **kwargs)
with open("registro.txt", "a") as f:
f.write(f"{func.__name__} fue llamado con los siguientes argumentos: {args}, {kwargs}\n")
return resultado
return wrapper
Para aplicar el decorador a una función, simplemente debemos colocar el símbolo @ seguido del nombre del decorador encima de la definición de la función que queremos decorar. Por ejemplo:
@my_decorator
def my_function():
print("Hello, world!")
En este ejemplo, la función my_function()
será decorada con el decorador my_decorator
. Al llamar a my_function()
, en realidad estaremos llamando a la función devuelta por el decorador, que a su vez llamará a la función original.
>>> my_function()
Before the function runs
Hello, world!
After the function runs
Podemos ver que el decorador ha añadido funcionalidad antes y después de la ejecución de la función original.
Last updated