Decoradores en Python

Los decoradores son una herramienta muy poderosa y útil en Python ya que permite a los programadores modificar el comportamiento de una función o clase. Los decoradores nos permiten envolver otra función para extender el comportamiento de la función envuelta, sin modificarla permanentemente. Pero antes de profundizar en los decoradores, comprendamos algunos conceptos que serán útiles para aprender los decoradores.

Objetos de primera clase
En Python, las funciones son objetos de primera clase, lo que significa que las funciones en Python se pueden usar o pasar como argumentos.
Propiedades de las funciones de primera clase:

  • Una función es una instancia del tipo de objeto.
  • Puede almacenar la función en una variable.
  • Puede pasar la función como un parámetro a otra función.
  • Puede devolver la función desde una función.
  • Puedes almacenarlos en estructuras de datos como tablas hash, listas,…

Considere los siguientes ejemplos para una mejor comprensión.

Ejemplo 1: Tratar las funciones como objetos. 

Python3

# Python program to illustrate functions
# can be treated as objects
def shout(text):
    return text.upper()
 
print(shout('Hello'))
 
yell = shout
 
print(yell('Hello'))

Producción:

HELLO
HELLO

En el ejemplo anterior, hemos asignado la función gritar a una variable. Esto no llamará a la función, sino que toma el objeto de función al que hace referencia un grito y crea un segundo nombre que apunta a él, grito.

Ejemplo 2: pasar la función como argumento 

Python3

# Python program to illustrate functions
# can be passed as arguments to other functions
def shout(text):
    return text.upper()
 
def whisper(text):
    return text.lower()
 
def greet(func):
    # storing the function in a variable
    greeting = func("""Hi, I am created by a function passed as an argument.""")
    print (greeting)
 
greet(shout)
greet(whisper)

Producción:

HI, I AM CREATED BY A FUNCTION PASSED AS AN ARGUMENT.
hi, i am created by a function passed as an argument.

En el ejemplo anterior, la función saludar toma otra función como parámetro (gritar y susurrar en este caso). La función pasada como argumento se llama dentro de la función saludar.

Ejemplo 3: Devolver funciones desde otra función.

Python3

# Python program to illustrate functions
# Functions can return another function
 
def create_adder(x):
    def adder(y):
        return x+y
 
    return adder
 
add_15 = create_adder(15)
 
print(add_15(10))

Producción:

25

En el ejemplo anterior, hemos creado una función dentro de otra función y luego hemos devuelto la función creada dentro.
Los tres ejemplos anteriores representan los conceptos importantes que se necesitan para comprender a los decoradores. Después de revisarlos, profundicemos ahora en los decoradores.

Decoradores

Como se indicó anteriormente, los decoradores se utilizan para modificar el comportamiento de una función o clase. En Decorators, las funciones se toman como argumento en otra función y luego se llaman dentro de la función contenedora.

Sintaxis para decorador: 

@gfg_decorator
def hello_decorator():
    print("Gfg")

'''Above code is equivalent to -

def hello_decorator():
    print("Gfg")
    
hello_decorator = gfg_decorator(hello_decorator)'''

En el código anterior, gfg_decorator es una función invocable, que agregará un código encima de otra función invocable, la función hello_decorator y devolverá la función contenedora.

El decorador puede modificar el comportamiento :  

Python3

# defining a decorator
def hello_decorator(func):
 
    # inner1 is a Wrapper function in
    # which the argument is called
     
    # inner function can access the outer local
    # functions like in this case "func"
    def inner1():
        print("Hello, this is before function execution")
 
        # calling the actual function now
        # inside the wrapper function.
        func()
 
        print("This is after function execution")
         
    return inner1
 
 
# defining a function, to be called inside wrapper
def function_to_be_used():
    print("This is inside the function !!")
 
 
# passing 'function_to_be_used' inside the
# decorator to control its behaviour
function_to_be_used = hello_decorator(function_to_be_used)
 
 
# calling the function
function_to_be_used()

Producción: 

Hello, this is before function execution
This is inside the function !!
This is after function execution

Veamos el comportamiento del código anterior y cómo se ejecuta paso a paso cuando se llama a la «función_a_usar».

Pasemos a otro ejemplo en el que podemos averiguar fácilmente el tiempo de ejecución de una función usando un decorador.

Python3

# importing libraries
import time
import math
 
# decorator to calculate duration
# taken by any function.
def calculate_time(func):
     
    # added arguments inside the inner1,
    # if function takes any arguments,
    # can be added like this.
    def inner1(*args, **kwargs):
 
        # storing time before function execution
        begin = time.time()
         
        func(*args, **kwargs)
 
        # storing time after function execution
        end = time.time()
        print("Total time taken in : ", func.__name__, end - begin)
 
    return inner1
 
 
 
# this can be added to any function present,
# in this case to calculate a factorial
@calculate_time
def factorial(num):
 
    # sleep 2 seconds because it takes very less time
    # so that you can see the actual difference
    time.sleep(2)
    print(math.factorial(num))
 
# calling the function.
factorial(10)

Producción: 

3628800
Total time taken in :  factorial 2.0061802864074707

¿Qué pasa si una función devuelve algo o se pasa un argumento a la función?

En todos los ejemplos anteriores, las funciones no devolvieron nada, por lo que no hubo ningún problema, pero es posible que necesite el valor devuelto.

Python3

def hello_decorator(func):
    def inner1(*args, **kwargs):
         
        print("before Execution")
         
        # getting the returned value
        returned_value = func(*args, **kwargs)
        print("after Execution")
         
        # returning the value to the original frame
        return returned_value
         
    return inner1
 
 
# adding decorator to the function
@hello_decorator
def sum_two_numbers(a, b):
    print("Inside the function")
    return a + b
 
a, b = 1, 2
 
# getting the value through return of the function
print("Sum =", sum_two_numbers(a, b))

Producción: 

before Execution
Inside the function
after Execution
Sum = 3

En el ejemplo anterior, puede notar una gran diferencia en los parámetros de la función interna. La función interna toma el argumento como *args y **kwargs, lo que significa que se puede pasar una tupla de argumentos posicionales o un diccionario de argumentos de palabras clave de cualquier longitud. Esto lo convierte en un decorador general que puede decorar una función con cualquier número de argumentos.

Enstringmiento de decoradores

En términos más simples, enstringr decoradores significa decorar una función con múltiples decoradores.

Ejemplo: 

Python3

# code for testing decorator chaining
def decor1(func):
    def inner():
        x = func()
        return x * x
    return inner
 
def decor(func):
    def inner():
        x = func()
        return 2 * x
    return inner
 
@decor1
@decor
def num():
    return 10
 
print(num())

Producción:

400

El ejemplo anterior es similar a llamar a la función como –

decor1(decor(num))

Publicación traducida automáticamente

Artículo escrito por Sheetansh kumar y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *