Bienvenido, les saluda Luis y hoy les traigo este tutorial.
Índice
Aprovecha los decoradores de Python en tu proyecto
Los decoradores son funciones que modifican el comportamiento de otras funciones sin cambiar sus operaciones centrales. Como lo indica el nombre, los decoradores solo decoran otras funciones.
Puede pensar en otras funciones como rosquillas simples, y lo que hacen los decoradores es aplicar diferentes revestimientos a las rosquillas . No importa el sabor que tengas (los decoradores), las donas (las funciones decoradas) siguen siendo donas.
El siguiente código muestra un decorador básico que registra el tiempo transcurrido de una llamada de función. En esencia, la función decoradora acepta otra función (es decir, la función a decorar) como argumento de entrada.
Define una función interna que realmente proporciona actividades de decoración y devuelve la función interna como salida. Para usar el decorador, simplemente coloque el nombre de la función decoradora con un @prefijo de signo encima de la función que desea decorar con la función decoradora.
Forma básica de decoradores
Ahora que tiene una buena comprensión de la forma más básica de decoradores, es hora de obtener un conocimiento más profundo sobre ellos.
Hay un problema con el fragmento de código anterior: se supone que las funciones decoradas no requieren ningún argumento de entrada (línea 7).
Si usamos el decorador en su forma actual con una función que toma un argumento, no funcionará como cabría esperar:
Incompatibilidad de firmas
Para abordar este problema, deberíamos considerar usar *args
y **kwargs
con la definición de decorador. Estos dos términos se utilizan para denotar un número indeterminado (de cero a más) de argumentos posicionales y de palabras clave en funciones.
En otras palabras, pueden capturar todo tipo de firmas de funciones. Veamos la versión modificada y su compatibilidad mejorada:
Soporte de diferentes firmas
Como puede ver, el cambio más grande es que en lugar de asumir que la función no toma argumentos, la versión revisada proporciona la llamada a la función *args
y **kwargs
de tal manera que el decorador es más versátil ahora.
Algunas personas pueden no saber que la decoración estropeará de forma predeterminada los metadatos de la función decorada, como las cadenas de documentos. Veamos el comportamiento del decorador actual:
Problema con las cadenas de documentos
Como puede ver, las cadenas de documentos muestran la función interna que definimos en la función decoradora pero no la función decorada say_hello
. Debajo del capó, todo se debe a que el proceso de decoración es crear un cierre a partir de la función de decorador.
En esencia, el proceso de decoración equivale a llamar say_hello = logging_time(say_hello)
. Por lo tanto, no es sorprendente que obtenga las cadenas de documentación de la función interna con la función decorada.
Para resolver este problema, podemos utilizar otra función decoradora (wraps
) que se envía en la biblioteca estándar de Python, como se muestra a continuación:
Envuelve la función decorada
- Línea 2: Importamos el
wraps
función decoradora de lafunctools
módulo. - Línea 6: Usamos el
wraps
decorador para decorar la función interior envolviendo la función a decorar (elfunc
argumento). - Líneas 21-23: la función decorada ahora tiene las cadenas de documentación correctas.
Además de los beneficios de pasar las cadenas de documentación esperadas para las funciones decoradas, el decorador wraps
es necesario para que la función decorada muestre las anotaciones de función correctas (por ejemplo, tipos de argumentos) y admita el decapado para la conservación de datos.
Hasta el momento, nuestros decoradores tienen fijadas sus funcionalidades de decoración. ¿Qué pasa si queremos que nuestros decoradores se comporten de manera diferente en función de las preferencias del usuario? En este caso, podemos considerar definir decoradores que acepten argumentos .
Continuemos con el ejemplo del decorador de registrar el tiempo transcurrido de las funciones. Supongamos una necesidad empresarial trivial: nuestros decoradores muestran el tiempo en la unidad que especifica el usuario (ya sea en milisegundos o segundos).
El siguiente código le muestra una posible solución:
Decoradores que aceptan parámetros
Como puede ver, para permitir que el decorador acepte el parámetro unit
, necesitamos crear otra capa fuera del decorador que definimos anteriormente. Veamos si funciona como esperamos:
Decoradores con parámetros
- Usamos dos configuraciones diferentes para el decorador y ambas funcionan como se esperaba.
- La razón para agregar otra capa para que el decorador acepte argumentos es que el proceso de decoración está encadenando la llamada a la función. Llamar
logging_time(“ms”)
nos permitiría obtener la funciónlogger
, que tiene exactamente la misma firma de función que la función decoradora que definimos anteriormente.
Tenga en cuenta que la definición actual de los decoradores requiere que especifiquemos la unidad para la decoración. Si desea que sus argumentos sean opcionales, necesita un trabajo adicional.
Los ejemplos anteriores solo han utilizado un decorador para decorar otras funciones. Sin embargo, es posible utilizar varios decoradores para decorar funciones al mismo tiempo. Para hacer eso, simplemente podemos apilar los decoradores encima de la función a decorar.
El siguiente fragmento de código muestra un ejemplo trivial:
Varios decoradores
El código anterior le muestra que creamos otro decorador que simplemente llama a la función decorada dos veces. Cabe destacar que definimos dos funciones que son decoradas por dos decoradores. Sin embargo, aplicamos los decoradores en un orden diferente, lo que provocará efectos distintos:
Efectos distintos del orden del decorador
- Notamos que la función
say_hi
se llama dos veces y el tiempo solo se registra una vez. Mientras tanto, la funciónsay_hello
se llama dos veces y el tiempo también se registra dos veces. - Cuando tenemos varios decoradores, el orden de aplicación de los decoradores se basa en la proximidad. En otras palabras, el que está justo encima de la función decorada es ejercer la decoración primero y así sucesivamente. Es por eso que el tiempo
say_hi
de la función se registra una vez, porque el decoradorlogging_time
se aplica en último lugar. Por el contrario, el decorador de repetición se aplica a la función que ya ha sido decorada porlogging_time
y, por lo tanto, el tiempo de la funciónsay_hello
se registra dos veces.
Hemos dicho que los decoradores son funciones. Para ser precisos, estas son funciones de orden superior, lo que significa que estas funciones usan otras funciones como argumentos de entrada y / o salida .
Sin embargo, ¿sabes que los decoradores se pueden implementar como clase? Con la posibilidad de tener decoradores como clases, deberíamos decir que los decoradores son invocables.
El siguiente código le muestra cómo podemos definir un decorador usando una clase:
Decoradores implementados como clase
- El ejemplo muestra no la forma más básica de decoradores, sino decoradores que pueden aceptar argumentos.
- Te dejo el reto de crear una clase que sirva de decoradora. El principio básico de implementar decoradores usando clases es el mismo que con las funciones de decorador regulares. Puede pensar en las clases como funciones porque la clase anterior se vuelve invocable al implementar el método
__call__
. - Como puede ver, al especificar el número de veces que se repite, la función decorada funciona como se esperaba.
Aunque es posible implementar decoradores usando clases, puede ser más complicado de lo que se presenta aquí si desea que sus decoradores sean completamente versátiles. El ejemplo dado es solo para proporcionarle una prueba de concepto.
Si desea utilizar estos decoradores con métodos que están definidos en una clase, debe considerar los argumentos posicionales relacionados con la clase (es decir cls
) o la instancia (es decir self
). Para obtener un conocimiento más profundo, puede consultar una buena discusión sobre Stack Overflow.
Cuando tenga un buen conocimiento de los decoradores, puede definir algunos personalizados (por ejemplo, tiempo de registro, verificación de tipos) que lo ayudarán con el trabajo de rutina.
Gracias por leer este artículo
Añadir comentario