Usar decoradores de Python para reintentar bloques de código
-
Importancia de los decoradores
reintentar
-
Use
@retry
para reintentar bloques de código en Python -
Use
tenacidad
para reintentar bloques de código en Python
Podemos modificar una función o clase con un decorador para extender el comportamiento de la función sin cambiarla permanentemente. Este artículo explica cómo utilizar los decoradores de reintento
para modificar una función existente sin realizar cambios en dicha función.
En este caso, la modificación vuelve a intentar la función varias veces en una situación dada donde su valor de retorno puede ser diferente al que queremos.
Importancia de los decoradores reintentar
Podemos usar decoradores para extender el comportamiento de una función específica, y podemos crear decoradores fácilmente para modificar esa función incluso cuando no tenemos acceso a ella o no queremos cambiarla.
Es posible que a menudo necesitemos esa función de una manera bastante específica, y ahí es donde entran los decoradores de Python. Entonces, creemos una función simple para mostrar cómo funcionan los decoradores.
La función simple, cociente()
, toma dos argumentos y divide el primer argumento por el segundo argumento.
def quotient(a, b):
return a / b
print(quotient(3, 7))
Producción :
0.42857142857142855
Sin embargo, si queremos que el resultado de la división sea siempre tal que el número mayor sea el que se divide (por lo que el resultado será 2.3333333333333335
), podemos cambiar el código o utilizar decoradores
.
Con decoradores, podemos extender el comportamiento de la función sin cambiar su bloque de código.
def improv(func):
def inner(a, b):
if a < b:
a, b = b, a
return func(a, b)
return inner
@improv
def quotient(a, b):
return a / b
print(quotient(3, 7))
Producción :
2.3333333333333335
La función improv()
es la función decoradora que toma la función cociente()
como argumento y contiene una función interna que toma el argumento de la función cociente()
y aporta la funcionalidad adicional que necesita agregar.
Ahora, con los decoradores, podemos agregar una funcionalidad de reintento
en una función en particular, especialmente con funciones a las que no tenemos acceso.
Los decoradores reintentar
son útiles en escenarios en los que pueden existir errores o comportamientos impredecibles, y desea volver a intentar la misma operación cuando ocurran.
Un ejemplo típico es el manejo de una solicitud fallida dentro de un bucle for
. En tales escenarios, podemos usar decoradores de reintento
para administrar el reintento de esa solicitud específica una cantidad específica de veces.
Use @retry
para reintentar bloques de código en Python
Para los decoradores de reintento
, existen diferentes bibliotecas que proporcionan esta función, y una de esas bibliotecas es la biblioteca de reintento
.
Con él, puede especificar las condiciones de esperar
y detener
desde Excepciones
a los resultados de retorno esperados. Para instalar la librería reintentar
, podemos usar el comando pip
de la siguiente manera:
pip install retrying
Ahora, vamos a crear una función que genere aleatoriamente números entre 0
y 10
, pero que genere un ValueError
cuando tengamos una situación en la que el número sea mayor que 1
.
import random
def generateRandomly():
if random.randint(0, 10) > 1:
raise ValueError("Number generated is greater than one")
else:
return "Finally Generated."
print(generateRandomly())
La salida del código tendrá el siguiente aspecto si el número generado en ese momento es mayor que uno.
Traceback (most recent call last):
File "c:\Users\akinl\Documents\Python\SFTP\test.py", line 11, in <module>
print(generateRandomly())
File "c:\Users\akinl\Documents\Python\SFTP\test.py", line 6, in generateRandomly
raise ValueError("Number generated is greater than one")
ValueError: Number generated is greater than one
No podemos tener una situación en la que se arroje un ValueError
en nuestro código, por lo que podemos introducir un decorador reintentar
para volver a intentar la función generateRandomly()
hasta que no genere un ValueError
.
import random
from retrying import retry
@retry
def generateRandomly():
if random.randint(0, 10) > 1:
raise ValueError("Number generated is greater than one")
else:
return "Finally Generated."
print(generateRandomly())
Producción :
Finally Generated.
Ahora, el decorador reintentar
vuelve a intentar la operación aleatoria
hasta que no tiene un ValueError
, y solo tenemos la cadena Finally Generated.
.
Podemos ver la cantidad de veces que el código volvió a intentar “generar aleatoriamente ()” introduciendo una instrucción print
dentro del bloque if
.
import random
from retrying import retry
@retry
def generateRandomly():
if random.randint(0, 10) > 1:
print("1")
raise ValueError("Number generated is greater than one")
else:
return "Finally Generated."
print(generateRandomly())
Producción :
1
1
1
1
1
1
1
Finally Generated.
Aquí, 8
, pero podría ser diferente cuando ejecutas el código. Sin embargo, no podemos tener una situación en la que el código siga intentándolo durante mucho tiempo. Entonces, tenemos argumentos como stop_max_attempt_number
y stop_max_delay
.
import random
from retrying import retry
@retry(stop_max_attempt_number=5)
def generateRandomly():
if random.randint(0, 10) > 1:
print("1")
raise ValueError("Number generated is greater than one")
else:
return "Finally Generated."
print(generateRandomly())
Producción :
1
1
1
Finally Generated.
La función se vuelve a intentar solo 5
veces, pero tiene éxito antes de la quinta vez, devuelve el valor Finalmente generado
, o no lo hace y arroja el ValueError
.
1
1
1
1
1
File "C:\Python310\lib\site-packages\retrying.py", line 247, in get
six.reraise(self.value[0], self.value[1], self.value[2])
File "C:\Python310\lib\site-packages\six.py", line 719, in reraise
raise value
File "C:\Python310\lib\site-packages\retrying.py", line 200, in call
attempt = Attempt(fn(*args, **kwargs), attempt_number, False)
File "c:\Users\akinl\Documents\Python\SFTP\test.py", line 9, in generateRandomly
raise ValueError("Number generated is greater than one")
ValueError: Number generated is greater than one
Use tenacidad
para reintentar bloques de código en Python
La biblioteca de reintentos
puede ser un poco peculiar y ya no se mantiene, pero la biblioteca de tenacidad
proporciona todas sus funciones con más herramientas a su disposición.
Para instalar tenacity
, utilice el siguiente comando pip
:
pip install tenacity
Podemos probar el mismo código con un intento de detener
en 3
.
import random
from tenacity import retry, stop_after_attempt
@retry(stop=stop_after_attempt(3))
def generateRandomly():
if random.randint(0, 10) > 1:
print("1")
raise ValueError("Number generated is greater than one")
else:
return "Finally Generated."
print(generateRandomly())
La salida del código si el número aleatorio generado es menor que 1 dentro del marco de tiempo de tres intentos.
1
Finally Generated.
Sin embargo, si no es así, se lanzará la siguiente salida.
1
1
1
Traceback (most recent call last):
File "C:\Python310\lib\site-packages\tenacity\__init__.py", line 407, in __call__
result = fn(*args, **kwargs)
File "c:\Users\akinl\Documents\Python\SFTP\test.py", line 9, in generateRandomly
raise ValueError("Number generated is greater than one")
ValueError: Number generated is greater than one
The above exception was a direct cause of the following exception:
Traceback (most recent call last):
File "c:\Users\akinl\Documents\Python\SFTP\test.py", line 14, in <module>
print(generateRandomly())
File "C:\Python310\lib\site-packages\tenacity\__init__.py", line 324, in wrapped_f
return self(f, *args, **kw)
File "C:\Python310\lib\site-packages\tenacity\__init__.py", line 404, in __call__
do = self.iter(retry_state=retry_state)
File "C:\Python310\lib\site-packages\tenacity\__init__.py", line 361, in iter
raise retry_exc from fut.exception()
tenacity.RetryError: RetryError[<Future at 0x29a75442c20 state=finished raised ValueError>]
Olorunfemi is a lover of technology and computers. In addition, I write technology and coding content for developers and hobbyists. When not working, I learn to design, among other things.
LinkedIn