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 nueva funcionalidad a un objeto ya existente sin 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 de forma din谩mica sin utilizar directamente subclases. Esta es una buena idea cuando desea ampliar la funcionalidad de una funci贸n que no desea modificar directamente. Los patrones de decorador se pueden implementar en todas partes, pero Python proporciona una sintaxis y caracter铆sticas m谩s expresivas para eso.

    En este art铆culo, discutiremos los decoradores de Python en detalle.

    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 gui贸n:

    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 lowercase que toma una funci贸n como argumento. Para probar nuestro lowercase funci贸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 fusionar los dos fragmentos de c贸digo anteriores en uno. Creamos la funci贸n hello_function() que devuelve la frase “HOLA MUNDO”. 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, existe una forma m谩s sencilla de aplicar decoradores en Python. Simplemente podemos agregar el @ s铆mbolo antes del nombre de la funci贸n decoradora justo encima de la funci贸n que se va a decorar. Por ejemplo:

    @lowercase
    def hello_function():
        return 'HELLO WORLD'
    
    print(hello_function())
    

    Salida

    hello world
    

    C贸mo aplicar varios 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 ejecutar铆a 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 nombre split_sentence. Apliquemos ahora lowercase y split_sentence decoradores 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 ha dividido en dos y se ha convertido en min煤sculas desde que aplicamos ambos lowercase y split_sentence decoradores para hello_function.

    Pasar 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, a su vez, se pasar谩n a la funci贸n decorada en 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 script anterior, el decorador acepta dos argumentos :, argument1 y argument1.

    Creaci贸n de decoradores de uso general

    Los decoradores de uso general se pueden aplicar a cualquier funci贸n. Este tipo de decoradores son muy 煤tiles para depurar, por ejemplo.

    Podemos definirlos usando el args y **kwargs argumentos. Todos los argumentos posicionales y de palabras clave se almacenan en estas dos variables, respectivamente. Con args y kwargs, podemos pasar cualquier n煤mero de argumentos durante la llamada a una 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 puede 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 a la 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 ajustar funciones. El cierre del contenedor 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 del contenedor. Demostremos esto:

    function_with_arguments.__name__
    

    Salida

    'my_wrapper'
    

    Esto presenta un gran desaf铆o durante la depuraci贸n. Sin embargo, Python proporciona functools.wraps decorador que puede ayudar a resolver este desaf铆o. Funciona copiando los metadatos perdidos a su cierre decorado.

    Ahora demostremos 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
    

    Desde que usamos functools.wraps en la funci贸n contenedora, podemos inspeccionar los metadatos de la funci贸n para “hello_function”:

    hello_function.__name__
    

    Salida

    'hello_function'
    
    hello_function.__doc__
    

    Salida

    'Saying hello'
    

    El script anterior muestra claramente que los metadatos ahora se refieren a la funci贸n en lugar de al contenedor. Te recomiendo que siempre uses functools.wraps siempre que est茅 definiendo un decorador. Esto le facilitar谩 la depuraci贸n.

    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, m茅todo o 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 los decoradores durante el desarrollo usando el functools m贸dulo.

     

    Etiquetas:

    Deja una respuesta

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