再谈装饰器
昨天我分享了装饰器的使用方法,发现看的人并不多,这也正常,毕竟装饰器是一种锦上添花的东西,没有他,无法稍微麻烦点,但还是可以凑合着过的。
其实,高手和普通人就差这一点,一般人觉得学得够当下所用了,也就不愿意再花时间学习了,这样也就不会再进步了,也就成不了高手。
虽然我也不是高手,但我愿意持续学习,缩短与高手之间得距离。
对于我们从事 IT 职业的,学习的东西一定要使用,如果工作上没有需求,那么就自己创造需求,自己来实现,只有这样,才能真正的学会。否则,当时懂了,时间一长,全忘了,花了时间,确毫无收获。
我很喜欢布尔值,要么是 0 要么是 1。学习也是一样,要么不学,要么就学到 100%。
下面,我们就来聊聊装饰器非常实用的应用场景。
我们写程序时都会处理异常,有些异常是需要抛出的,有些异常是可以忽略的,还有些异常通过重跑几次就解决了。假如让你写个装饰器,当被装饰的函数调用抛出指定的异常时,函数会被重新调用,直到达到指定的最大调用次数才重新抛出指定的异常,你怎么写呢?
假如有以下函数 func
import time
class ValueError(Exception):
pass
class CustomException(Exception):
pass
def func(num):
time.sleep(1)
print("func is called.")
if num == 0:
pass
elif num == 1:
raise CustomException
elif num == 2:
raise ValueError
else:
raise Exception
那么:
@retry(times=3,traced_exceptions=ValueError,reraised_exception=CustomException)
def func(num):
就表示当 func 抛出 ValueError 时自动重试 3 次,如果最后抛出的是 CustomException 就抛出异常,否则就什么也不抛出。我们还可以稍微增加点难度,比如:
traced_exceptions 为监控的异常,可以为 None(默认)、异常类、或者一个异常类的列表。 traced_exceptions 如果为 None,则监控所有的异常;如果指定了异常类,则若函数调用抛出指定的异常时,重新调用函数,直至成功返回结果或者达到最大尝试次数,此时重新抛出原异常(reraised_exception 的值为 None)
,或者抛出由 reraised_exception 指定的异常。
你可以自己先实现下,下面给出我自己的一种实现方法:
def retry(times=10, traced_exceptions=None, reraised_exception=None):
'''设计一个装饰器函数 retry,当被装饰的函数调用抛出指定的异常时,
函数会被重新调用,直到达到指定的最大调用次数才重新抛出指定的异常。
traced_exceptions 为监控的异常,可以为 None(默认)、异常类、或者一个异常类的列表。
traced_exceptions 如果为 None,则监控所有的异常;如果指定了异常类,则若函数调用抛出指定的异常时,重新调用函数,直至成功返回结果
或者达到最大尝试次数,此时重新抛出原异常(reraised_exception 的值为 None)
,或者抛出由 reraised_exception 指定的异常。
'''
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
num = times
need_raisse = False
while True:
try:
return func(*args, **kwargs)
except Exception as e:
if traced_exceptions is None:#说明要捕捉所有异常,直接 pass
num -=1
elif isinstance(e,traced_exceptions):#如果指定了捕捉的异常类,则 pass
num -= 1
elif type(traced_exceptions) == list and type(e) in traced_exceptions:#如果指定了捕捉异常类的列表,则 pass
num -= 1
else: #需要抛出异常
need_raisse = True
if num == 0 or need_raisse:#重试次数完毕或非捕捉的异常类
if reraised_exception is None or type(e) == reraised_exception:
#reraised_exception 为 None 则抛出原来的异常,否则只抛出指定的异常
raise
else:
break
return wrapper
return decorator
给出一种运行结果:
@retry(times=3,traced_exceptions=ValueError,reraised_exception=ValueError)
对应的结果如下:
func is called.
func is called.
func is called.
Traceback (most recent call last):
File "E:/test.py", line 65, in <module>
func(2)
File "E:/test.py", line 29, in wrapper
return func(*args, **kwargs)
File "E:/test.py", line 60, in func
raise ValueError
__main__.ValueError
当你实现这个装饰器后,可以保存下来,后续的项目中肯定可以用得到,到时候就不用再造轮子了。
如果你还不太理解装饰器的作用,请参考我的上篇文章我是装饰器。
其他应用场景
1、代码都写好了,现在要求插入日志。
2、代码都写好了,现在要求加入计时功能、性能测试。
3、代码都写好了,现在要求一个函数变成事务性操作。
4、代码都写好了,现在又要求增加权限验证。
有了装饰器,随你需求怎么变吧,反正我不改原有代码,就可以实现你的需求。
(完)