Decoradores Python - Codelivly

Un decorador acepta una función y le agrega funcionalidad antes de devolverla. Aprenderá cómo crear un decorador y por qué debería usarlo en este artículo.

Índice
  1. Decoradores en Python
  2. Requisitos previos para aprender decoradores
  3. volver a decoradores
  4. Decorar características con parámetros
  5. Encadenamiento de decoradores en Python
    1. Relacionados

Decoradores en Python

Decoradores son una herramienta muy poderosa y útil en Python ya que permite a los programadores modificar el comportamiento de una función o una clase. Los decoradores nos permiten envolver otra función para extender su comportamiento sin tener que modificarla permanentemente. Pero, antes de profundizar demasiado en los decoradores, primero comprendamos algunos principios que nos ayudarán a estudiar a los decoradores.

Requisitos previos para aprender decoradores

Para comprender a los decoradores, primero debemos conocer algunos conceptos básicos de Python.

Tenemos que aceptar el hecho de que todo en Python es un objeto (sí, incluidas las clases). Los nombres que damos a estas cosas son solo identificadores. Las funciones no están exentas; ellos también son objetos (con atributos). Un único objeto de función puede tener varios nombres asignados.

Aquí un ejemplo.

def first(msg):
    print(msg)


first("Hello")

second = first
second("Hello")

Salir

Hello
Hello

Cuando ejecuta el código, la primera y la segunda función producen el mismo resultado. El primer y segundo nombre aquí corresponden al mismo objeto de función.

Ahora las cosas empiezan a ponerse más raras.

Las funciones se pueden pasar como argumentos a otra función.

Si usaste funciones como map, filter y reduce en Python, entonces ya lo sabes.

Las funciones de orden superior se definen como funciones que toman otras funciones como argumentos. Aquí hay un ejemplo de una función como esta.

def inc(x):
    return x + 1


def dec(x):
    return x - 1


def operate(func, x):
    result = func(x)
    return result

Invocamos la función de la siguiente manera.

>>> operate(inc,3)
4
>>> operate(dec,3)
2

Además, una función puede devolver otra función.

def is_called():
    def is_returned():
        print("Hello")
    return is_returned


new = is_called()

# Outputs "Hello"
new()

Salir

Hello

Aquí mismo, is_returned() es una función anidada que se define y se devuelve cada vez que llamamos is_called().

Finalmente, necesitamos saber acerca de los cierres en Python.


volver a decoradores

Las funciones y los métodos se llaman invocable como pueden ser llamados.

Invocable se refiere a cualquier objeto que implementa el especial llamada() método. Un decorador es, en el sentido más básico, un invocable que devuelve un invocable.

Básicamente, un decorador toma una función, agrega funcionalidad y la devuelve.

def make_pretty(func):
    def inner():
        print("I got decorated")
        func()
    return inner


def ordinary():
    print("I am ordinary")

Cuando ejecuta los siguientes códigos en el shell,

>>> ordinary()
I am ordinary

>>> # let's decorate this ordinary function
>>> pretty = make_pretty(ordinary)
>>> pretty()
I got decorated
I am ordinary

En el ejemplo anterior, make_pretty() es decorador En el paso de asignación:

pretty = make_pretty(ordinary)

La funcion ordinary() fue decorado y la función devuelta recibió el nombre pretty.

La función de decorador, como podemos ver, ha aportado nuevas funcionalidades a la función original. Es como envolver un regalo. El decorador realiza la función de un envoltorio. La naturaleza del objeto decorado (la realidad presente en el interior) permanece invariable. Sin embargo, ahora parece ser atractivo (ya que ha sido decorado).

Generalmente, decoramos una función y la reasignamos como,

ordinary = make_pretty(ordinary).

Esta es una construcción común y por esta razón Python tiene una sintaxis para simplificar esto.

Podemos colocar el símbolo @ sobre la definición de la función a decorar, junto con el nombre de la función decoradora. Por ejemplo,

@make_pretty
def ordinary():
    print("I am ordinary")

es equivalente a

def ordinary():
    print("I am ordinary")
ordinary = make_pretty(ordinary)

Es solo azúcar sintáctico para implementar decoradores.

Decorar características con parámetros

El decorador anterior era simple y solo trabajaba con funciones sin parámetros. ¿Qué pasaría si hubiera funciones que tomaran parámetros como:

def divide(a, b):
    return a/b

Esta función tiene dos parámetros, a y B. Sabemos que dará error si aprobamos B como 0

>>> divide(2,5)
0.4
>>> divide(2,0)
Traceback (most recent call last):
...
ZeroDivisionError: division by zero

Ahora vamos a crear un decorador para verificar este caso que causará el error.

def smart_divide(func):
    def inner(a, b):
        print("I am going to divide", a, "and", b)
        if b == 0:
            print("Whoops! cannot divide")
            return

        return func(a, b)
    return inner


@smart_divide
def divide(a, b):
    print(a/b)

Esta nueva implementación volverá None si se produce la condición de error.

>>> divide(2,5)
I am going to divide 2 and 5
0.4

>>> divide(2,0)
I am going to divide 2 and 0
Whoops! cannot divide

De esta forma podemos decorar funciones que toman parámetros.

Los parámetros de la función inner() anidada dentro del decorador son los mismos que los parámetros de las funciones que decora, como descubriría un observador cuidadoso. Con eso en mente, ahora podemos crear decoradores universales que pueden manejar cualquier cantidad de parámetros.

En Python, esta magia se hace como

function(*args, **kwargs). De esta manera, args será la tupla de los argumentos posicionales y kwargs será el diccionario de argumentos de palabras clave. Un ejemplo de tal decorador será:

def works_for_all(func):
    def inner(*args, **kwargs):
        print("I can decorate any function")
        return func(*args, **kwargs)
    return inner

Encadenamiento de decoradores en Python

Se pueden encadenar varios decoradores en Python.

Esto significa que una función se puede decorar varias veces con decoradores diferentes (o idénticos). Los decoradores simplemente se colocan sobre la función deseada.

def star(func):
    def inner(*args, **kwargs):
        print("*" * 30)
        func(*args, **kwargs)
        print("*" * 30)
    return inner


def percent(func):
    def inner(*args, **kwargs):
        print("%" * 30)
        func(*args, **kwargs)
        print("%" * 30)
    return inner


@star
@percent
def printer(msg):
    print(msg)


printer("Hello")

Salir

******************************
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Hello
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
******************************

La sintaxis anterior de,

@star
@percent
def printer(msg):
    print(msg)

es equivalente a

def printer(msg):
    print(msg)
printer = star(percent(printer))

El orden en el que encadenamos a los decoradores es importante. Si invertimos el orden como,

@percent
@star
def printer(msg):
    print(msg)

La salida sería:

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
******************************
Hello
******************************
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

"Gracias por leer"

Si quieres conocer otros artículos parecidos a Decoradores Python - Codelivly puedes visitar la categoría Tutoriales.

Deja un comentario

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

Subir

Esta página web utiliza cookies para analizar de forma anónima y estadística el uso que haces de la web, mejorar los contenidos y tu experiencia de navegación. Para más información accede a la Política de Cookies . Ver mas