Python | Función functools.wraps()

functools es un módulo estándar de Python para funciones de orden superior (funciones que actúan sobre otras funciones o las devuelven). wraps() es un decorador que se aplica a la función de envoltura de un decorador. Actualiza la función contenedora para que parezca una función envuelta copiando atributos como __name__, __doc__ (la string de documentación), etc.
 

Sintaxis: @functools.wraps(envuelto, asignado = WRAPPER_ASSIGNMENTS, actualizado = WRAPPER_UPDATES)
Parámetros:  
envuelto : El nombre de la función que será decorado por la función contenedora. 
asignado : tupla para especificar qué atributos de la función original se asignan directamente a los atributos coincidentes en la función contenedora. De forma predeterminada, se establece en WRAPPER_ASSIGNMENTS (que asigna a la función contenedora __module__, __name__, __qualname__, __annotations__ y __doc__, la string de documentación) 
actualizado: Tupla para especificar qué atributos de la función contenedora se actualizan con los atributos correspondientes de la función original. De forma predeterminada, se establece en WRAPPER_UPDATES (que actualiza el __dict__ de la función contenedora, es decir, el diccionario de instancias). 
 

Ejemplo 1: Sin functools.wraps()
 

Python3

def a_decorator(func):
    def wrapper(*args, **kwargs):
        """A wrapper function"""
        # Extend some capabilities of func
        func()
    return wrapper
 
@a_decorator
def first_function():
    """This is docstring for first function"""
    print("first function")
 
@a_decorator
def second_function(a):
    """This is docstring for second function"""
    print("second function")
 
print(first_function.__name__)
print(first_function.__doc__)
print(second_function.__name__)
print(second_function.__doc__)
Producción:

wrapper
A wrapper function
wrapper
A wrapper function

 

Ahora, ¿qué sucederá si escribimos ayuda (primera_función) y ayuda (segunda_función)?
 

Python3

print("First Function")
help(first_function)
 
print("\nSecond Function")
help(second_function)
Producción: 

First Function
Help on function wrapper in module __main__:

wrapper(*args, **kwargs)
    A wrapper function


Second Function
Help on function wrapper in module __main__:

wrapper(*args, **kwargs)
    A wrapper function

 

Si bien el código anterior funcionará lógicamente bien, considere esto si está escribiendo una API o una biblioteca y alguien quiere saber qué hace su función y su nombre o simplemente escriba ayuda (su función), siempre mostrará el nombre de la función contenedora y la string de documentación . Esto se vuelve más confuso si ha utilizado la misma función contenedora para diferentes funciones, ya que mostrará los mismos detalles para cada una de ellas. 
Idealmente, debería mostrar el nombre y la string de documentación de la función envuelta en lugar de la función de envoltura. La solución manual sería asignar los atributos __name__, __doc__ en la función de ajuste antes de devolverlo.
 

Python3

def a_decorator(func):
    def wrapper(*args, **kwargs):
        """A wrapper function"""
        # Extend some capabilities of func
        func()
    wrapper.__name__ = func.__name__
    wrapper.__doc__ = func.__doc__
    return wrapper
 
@a_decorator
def first_function():
    """This is docstring for first function"""
    print("first function")
 
@a_decorator
def second_function(a):
    """This is docstring for second function"""
    print("second function")
 
print(first_function.__name__)
print(first_function.__doc__)
print(second_function.__name__)
print(second_function.__doc__)
Producción:

first_function
This is docstring for first function
second_function
This is docstring for second function

 

Esto resuelve el problema, pero ¿y si volvemos a escribir help(yourFunction),
 

Python3

print("First Function")
help(first_function)
 
print("\nSecond Function")
help(second_function)

Para primera_función: ayuda(primera_función) 
 

Producción: 

First Function
Help on function first_function in module __main__:

first_function(*args, **kwargs)
    This is docstring for first function


Second Function
Help on function second_function in module __main__:

second_function(*args, **kwargs)
    This is docstring for second function

 

Como puede ver, todavía tiene un problema, es decir, la firma de la función, muestra la firma utilizada por la función contenedora (aquí, firma genérica) para cada una de ellas. Además, si está implementando muchos decoradores, debe escribir estas líneas para cada uno de ellos. 
Entonces, para ahorrar tiempo y aumentar la legibilidad, podríamos usar functools.wraps() como decorador para la función contenedora .
Ejemplo (con functools.wraps()) 
 

Python3

from functools import wraps
 
def a_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        """A wrapper function"""
 
        # Extend some capabilities of func
        func()
    return wrapper
 
@a_decorator
def first_function():
    """This is docstring for first function"""
    print("first function")
 
@a_decorator
def second_function(a):
    """This is docstring for second function"""
    print("second function")
 
print(first_function.__name__)
print(first_function.__doc__)
print(second_function.__name__)
print(second_function.__doc__)
Producción: 

first_function
This is docstring for first function
second_function
This is docstring for second function

 

Ahora, si escribimos help(first_function) –
 

Python3

print("First Function")
help(first_function)
 
print("\nSecond Function")
help(second_function)
Producción: 

First Function
Help on function first_function in module __main__:

first_function()
    This is docstring for first function


Second Function
Help on function second_function in module __main__:

second_function(a)
    This is docstring for second function

 

Publicación traducida automáticamente

Artículo escrito por manishkhurana 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 *