使用Python装饰器来重试代码块
我们可以用装饰器来修改一个函数或类,以扩展该函数的行为,而不永久改变它。本文讨论了如何使用retry
装饰器来修改一个现有的函数,而不对上述函数进行修改。
在这种情况下,修改后的函数在给定的情况下重试几次,其返回值可能与我们想要的不同。
retry
装饰器的重要性
我们可以使用装饰器来扩展特定函数的行为,我们可以很容易地创建装饰器来修改该函数,即使我们不能访问它或不想改变它。
我们可能经常需要那个函数相当具体的方式,这就是Python装饰器的用处。所以让我们创建一个简单的函数来展示装饰器的工作原理。
这个简单的函数,quotient()
,接受两个参数并将第一个参数除以第二个参数。
def quotient(a, b):
return a / b
print(quotient(3, 7))
输出:
0.42857142857142855
然而,如果我们希望除法的结果总是被除数较大(所以结果将是2.3333333333333335
),我们可以改变代码或利用decorators
。
通过装饰器,我们可以扩展函数的行为而不改变其代码块。
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))
输出:
2.3333333333333335
improv()
函数是装饰器函数,它以quotient()
函数为参数,并持有一个内部函数,该函数接受quotient()
函数的参数,并带来你需要添加的额外功能。
现在,通过装饰器,我们可以在某个特定的函数上添加retry
功能,特别是对于我们无法访问的函数。
retry
decorators在可能存在不可预测的行为或错误的情况下很有帮助,当它们发生时,你想再次重试那个相同的操作。
一个典型的例子是在一个for
循环中处理一个失败的请求。在这种情况下,我们可以使用retry
装饰器来管理重试该特定请求的指定次数。
在 Python 中使用@retry
来重试代码块
对于retry
装饰器,有不同的库提供这种功能,其中一个库是retrying
库。
有了它,你可以从Exceptions
,指定wait
和stop
的条件,以预期返回结果。要安装retrying
库,我们可以使用pip
命令,如下所示:
pip install retrying
现在,让我们创建一个函数,随机创建0
和10
之间的数字,但当我们遇到数字大于1
的情况时,会引发一个ValueError
。
import random
def generateRandomly():
if random.randint(0, 10) > 1:
raise ValueError("Number generated is greater than one")
else:
return "Finally Generated."
print(generateRandomly())
如果当时生成的数字大于1,那么代码输出将如下所示。
Traceback (most recent call last):
File "c:UsersakinlDocumentsPythonSFTPtest.py", line 11, in <module>
print(generateRandomly())
File "c:UsersakinlDocumentsPythonSFTPtest.py", line 6, in generateRandomly
raise ValueError("Number generated is greater than one")
ValueError: Number generated is greater than one
我们的代码中不能出现抛出ValueError
的情况,所以我们可以引入一个retry
装饰器来重试generateRandomly()
函数,直到它没有引发一个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())
输出:
Finally Generated.
现在,retry
装饰器重试random
操作,直到它没有出现ValueError
,而我们只有字符串Finally Generated.
。
我们可以通过在if
块中引入一个print()
语句来查看代码重试generateRandomly()
的次数。
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())
输出:
1
1
1
1
1
1
1
Finally Generated.
在这里,8
,但当你运行代码时,它可能是不同的。然而,我们不能出现代码长时间不断重试的情况。所以,我们有stop_max_attempt_number
和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())
输出:
1
1
1
Finally Generated.
该函数只重试了5
次,但要么在第五次之前获得成功,返回值Finally Generated.
,要么不成功,抛出ValueError
。
1
1
1
1
1
File "C:Python310libsite-packagesretrying.py", line 247, in get
six.reraise(self.value[0], self.value[1], self.value[2])
File "C:Python310libsite-packagessix.py", line 719, in reraise
raise value
File "C:Python310libsite-packagesretrying.py", line 200, in call
attempt = Attempt(fn(*args, **kwargs), attempt_number, False)
File "c:UsersakinlDocumentsPythonSFTPtest.py", line 9, in generateRandomly
raise ValueError("Number generated is greater than one")
ValueError: Number generated is greater than one
在Python中使用tenacity
来重试代码块
retrying
库可能有点古怪,而且不再维护,但tenacity
库提供了它的所有功能,并有更多的工具可供使用。
要安装tenacity
,使用下面的pip
命令:
pip install tenacity
我们可以用stop
尝试同样的代码,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())
如果在三次尝试的时间范围内,生成的随机数小于1,则代码的输出。
1
Finally Generated.
然而,如果不是,就会抛出下面的输出。
1
1
1
Traceback (most recent call last):
File "C:Python310libsite-packagestenacity__init__.py", line 407, in __call__
result = fn(*args, **kwargs)
File "c:UsersakinlDocumentsPythonSFTPtest.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:UsersakinlDocumentsPythonSFTPtest.py", line 14, in <module>
print(generateRandomly())
File "C:Python310libsite-packagestenacity__init__.py", line 324, in wrapped_f
return self(f, *args, **kw)
File "C:Python310libsite-packagestenacity__init__.py", line 404, in __call__
do = self.iter(retry_state=retry_state)
File "C:Python310libsite-packagestenacity__init__.py", line 361, in iter
raise retry_exc from fut.exception()
tenacity.RetryError: RetryError[<Future at 0x29a75442c20 state=finished raised ValueError>]