Introducción a los decoradores de Python

Introducción

En Python, un decorador es un patrón de diseño que podemos usar para agregar una nueva funcionalidad a un objeto ya existente sin la necesidad de modificar su estructura. Se debe llamar a un decorador directamente antes de la función que se va a extender. Con los decoradores, puede modificar la funcionalidad de un método, una función o una clase dinámicamente sin usar subclases directamente. Esta es una buena idea cuando desea ampliar la funcionalidad de una función que no desea modificar directamente. Los patrones de decorator pueden implementarse en cualquier lugar, pero Python proporciona una sintaxis y características más expresivas para eso.
En este artículo, discutiremos en detalle a los decoradores de Python.

Cómo crear decoradores

Veamos cómo se pueden crear decoradores en Python. Como ejemplo, crearemos un decorador que podemos usar para convertir la cadena de salida de una función en minúsculas. Para hacerlo, necesitamos crear una función decoradora y debemos definir una envoltura dentro de ella. Mira el siguiente script:
def lowercase(func):  
    def wrapper():
        func_ret = func()
        change_to_lowercase = func_ret.lower()
        return change_to_lowercase

    return wrapper
En el script anterior, simplemente hemos creado un decorador llamado lowercaseque toma una función como argumento. Para probar nuestra lowercasefunción necesitamos crear una nueva función y luego pasarla a este decorador. Tenga en cuenta que dado que las funciones son de primera clase en Python, puede asignar la función a una variable o tratarla como una. Emplearemos este truco para llamar a la función decoradora:
def hello_function():  
    return 'HELLO WORLD'

decorate = lowercase(hello_function)  
print(decorate())  
Salida
hello world  
Tenga en cuenta que puede combinar las dos piezas de código anteriores en una sola. Creamos la función hello_function()que devuelve la frase "HOLA MUNDIAL". Luego llamamos al decorador y pasamos el nombre de esta función como argumento mientras lo asignamos a la variable "decorar". Cuando se ejecuta, puede ver que la oración resultante se convirtió en minúsculas.
Sin embargo, hay una forma más fácil de aplicar decoradores en Python. Simplemente podemos agregar el @símbolo antes del nombre de la función de decoración justo encima de la función a decorar. Por ejemplo:
@lowercase
def hello_function():  
    return 'HELLO WORLD'

print(hello_function())  
Salida
hello world  

Cómo aplicar múltiples decoradores a una función

Python nos permite aplicar más de un decorador a una sola función. Para hacer esto correctamente, asegúrese de aplicar los decoradores en el mismo orden en que los ejecutó como código normal. Por ejemplo, considere el siguiente decorador:
def split_sentence(func):  
    def wrapper():
        func_ret = func()
        output = func_ret.split()
        return output

    return wrapper
Aquí hemos creado un decorador que toma una oración de entrada y la divide en varias partes. Al decorador se le ha dado el nam split_sentenceAhora apliquemos lowercasesplit_sentencedecoradores a una función.
Para ejecutar estas operaciones en el orden correcto, aplíquelas de la siguiente manera:
@split_sentence
@lowercase
def hello_function():  
    return 'HELLO WORLD'
print(hello_function())  
Salida
['hello', 'world']
Nuestra oración se dividió en dos y se convirtió en minúsculas desde que aplicamos ambos lowercasesplit_sentencedecoradores hello_function.

Pasando Argumentos a las Funciones del Decorador

Los decoradores de Python también pueden interceptar los argumentos que se pasan a las funciones decoradas. Los argumentos se pasarán a la función decorada en el tiempo de ejecución. Considere el siguiente ejemplo:
def my_decorator(func):  
    def my_wrapper(argument1, argument2):
        print("The arguments are: {0}, {1}".format(argument1, argument2))
        func(argument1, argument2)
    return my_wrapper


@my_decorator
def names(firstName, secondName):  
    print("Your first and second names are {0} and {1} respectively".format(firstName, secondName))

print(names("Nicholas", "Samuel"))  
Salida
The arguments are: Nicholas, Samuel  
Your first and second names are Nicholas and Samuel respectively  
En el guión anterior, el decorador acepta dos argumentos :, argument1argument1.

Creación de decoradores de propósito general

Los decoradores de propósito general se pueden aplicar a cualquier función. Este tipo de decoradores son muy útiles para propósitos de depuración, por ejemplo.
Podemos definirlos utilizando los argumentos args**kwargsTodos los argumentos posicionales y de palabras clave se almacenan en estas dos variables, respectivamente. Con argskwargs, podemos pasar cualquier número de argumentos durante una llamada de función. Por ejemplo:
def my_decorator(func):  
    def my_wrapper(*args, **kwargs):
        print('Positional arguments:', args)
        print('Keyword arguments:', kwargs)
        func(*args)
    return my_wrapper

@my_decorator
def function_without_arguments():  
    print("No arguments")

function_without_arguments()  
Salida
Positional arguments: ()  
Keyword arguments: {}  
No arguments  
Como puedes ver, no se pasaron argumentos al decorador.
Ahora veamos cómo podemos pasar valores a los argumentos posicionales:
@my_decorator
def function_with_arguments(x, y, z):  
    print(x, y, z)

function_with_arguments(5, 15, 25)  
Salida
Positional arguments: (5, 15, 25)  
Keyword arguments: {}  
5 15 25  
Hemos pasado tres argumentos posicionales al decorador. Para pasar argumentos de palabras clave, tenemos que usar palabras clave en la llamada de función. Aquí hay un ejemplo:
@my_decorator
def passing_keyword_arguments():  
    print("Passing keyword arguments")

passing_keyword_arguments(firstName="Nicholas", secondName="Samuel")  
Salida
Positional arguments: ()  
Keyword arguments: {'secondName': 'Samuel', 'firstName': 'Nicholas'}  
Passing keyword arguments  
Se pasaron dos argumentos de palabras clave al decorador.
En la siguiente sección, discutiremos cómo depurar decoradores.

Cómo depurar decoradores

En este punto, debe haber visto que usamos decoradores para envolver funciones. El cierre de la envoltura oculta el nombre de la función original, su lista de parámetros y la cadena de documentos.
Por ejemplo: si intentamos obtener los metadatos para el decorador function_with_arguments, obtendremos los metadatos del cierre de la envoltura. Vamos a demostrar esto:
function_with_arguments.__name__  
Salida
'my_wrapper'  
Esto presenta un gran desafío durante la depuración. Sin embargo, Python proporciona el functools.wrapsdecorador que puede ayudar a resolver este desafío. Funciona copiando los metadatos perdidos a su cierre decorado.
Ahora vamos a demostrar cómo funciona esto:
import functools

def lowercase(func):  
    @functools.wraps(func)
    def my_wrapper():
        return func().lower()
    return my_wrapper
@lowercase
def hello_function():  
    "Saying hello"
    return 'HELLO WORLD'

print(hello_function())  
Salida
hello world  
Como usamos functools.wrapsen la función de envoltura, podemos inspeccionar los metadatos de la función para "hello_function":
hello_function.__name__  
Salida
'hello_function'  
hello_function.__doc__  
Salida
'Saying hello'  
La secuencia de comandos anterior muestra claramente que los metadatos ahora se refieren a la función en lugar de a la envoltura. Te recomiendo que uses siempre functools.wrapscuando estés definiendo un decorador. Esto hará que la depuración sea mucho más fácil para usted.

Conclusión

El propósito de los decoradores es cambiar la funcionalidad de una clase, método o función dinámicamente sin usar subclases directamente o cambiar el código fuente de la clase, el método o la función que necesitamos decorar. En este artículo, vimos cómo crear decoradores simples y de propósito general y cómo pasar argumentos a los decoradores. También vimos cómo depurar a los decoradores durante el desarrollo utilizando el functoolsmódulo.

Acerca de: Programator

Somos Instinto Programador

0 comentarios:

Publicar un comentario

Dejanos tu comentario para seguir mejorando!

Con tecnología de Blogger.