Decoradores: ¿Por qué necesita en su código Python?

decoradores

Decoradores: ¿Por qué necesita en su código Python?

En este artículo, le enseñaré a usar decoradores en Python para que tu código sea legible y limpio. Python es elogiado por su claridad y dulzura sintáctica.

La metaprogramación es clave.


¿Qué son los decoradores?

Para comprender qué son los decoradores, primero debe estar familiarizado con la forma en que Python maneja las funciones. Desde su punto de vista, las funciones no son diferentes a los objetos regulares. Tienen propiedades y se pueden reasignar:

decoradores

Además, puede pasarlos como argumentos a otras funciones:

Ahora, a los decoradores. Los decoradores se usan para modificar el comportamiento de una función o clase. La forma en que esto se logra es definiendo una función (decorador) que devuelve otra función. Esto suena complicado, pero entenderá todo con este ejemplo:

decoradores

Vayamos paso a paso:

En primer lugar, definimos la función  logging_decorator en la línea 1. Acepta un solo argumento, que es la función que estamos tratando de decorar.

En el interior, definimos otra función: logging_wrapper. Luego, el logging_wrapper se devuelve y se usa en lugar de la función decorada original.

En la línea 7, puede ver cómo se aplica el decorador a la función de sum.

En la línea 11, cuando llamamos sum, no solo llamará sum. Llamará al logging_wrapper, que registrará antes y después de llamar a la sum.

 

¿Por qué necesita decoradores?

Es simple: legibilidad. Python es elogiado por su sintaxis clara y concisa, y los decoradores no son una excepción. Si hay algún comportamiento que sea común a más de una función, probablemente necesite crear un decorador. A continuación, se muestran algunos ejemplos de situaciones en las que pueden resultar útiles:

  • Comprobando el tipo de argumento en tiempo de ejecución
  • Llamadas a funciones de referencia
  • Resultados de la función de caché
  • Contar llamadas a funciones
  • Verificación de metadatos (permisos, roles, etc.)
  • Metaprogramación
  • Y mucho más…

Ahora enumeraré algunos ejemplos de código.

 

Decoradores con valores de retorno

Supongamos que queremos saber cuánto tarda cada llamada a función. Además, las funciones devuelven algo la mayor parte del tiempo, por lo que el decorador también debe manejar eso:

decoradores

Puede ver que almacenamos el valor devuelto en el result en la línea 5. Pero antes de devolverlo, tenemos que terminar de cronometrar la función. Este es un ejemplo de comportamiento que no sería posible sin los decoradores.

 

Decoradores con argumentos

A veces, queremos un decorador que acepte valores (como @app.route('/login') en Flask):

Para lograr eso, definimos una función adicional que acepta un argumento y devuelve un decorador.

 

Decorando con clases

Es posible decorar usando clases en lugar de funciones. La única diferencia es la sintaxis, así que haga lo que le resulte más cómodo. Aquí está el decorador de registros reescrito usando clases:

La ventaja es que no tiene que lidiar con funciones anidadas. Todo lo que necesita hacer es definir una clase y anular el método __call__.

 

Clases de decoración

Puede haber ocasiones en las que desee decorar todos y cada uno de los métodos de una clase. Siempre puedes escribirlo así:

Pero si tiene muchos métodos, esto puede salirse de control. Afortunadamente, hay una forma de decorar toda la clase a la vez:

 

[Click aquí]

 

Ahora, que no cunda el pánico. Esto parece complicado, pero esta es la misma lógica:

En primer lugar, dejamos logging_decorator como está. Se aplicará a todos los métodos de una clase.

Luego definimos un nuevo decorador:  log_all_class_methods. Es como un decorador normal, pero en su lugar devuelve una clase.

NewCls tiene un __getattribute__ personalizado. Para todas las llamadas a la clase original, decorará las funciones con  logging_decorator.

 

Decoradores empotrados

No solo puede definir sus propios decoradores, sino que también se envían algunos en la biblioteca estándar. Enumeraré los tres con los que más he trabajado:

@property: un decorador de elementos integrados que le permite definir captadores y definidores para propiedades de clase.

@lru_cache: un decorador del módulo  functools. Memoriza los argumentos de la función y los valores de retorno, lo cual es útil para funciones puras (como el  factorial).

@abstractmethod: un decorador del módulo abc. Indica que el método es abstracto y faltan detalles de implementación.

 

Notas de cierre

Gracias por leer, espero que les haya gustado esteartículo. ¡Manténgase suscrito para obtener más contenido!