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:
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:
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:
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:
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!