首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >再谈装饰器

再谈装饰器

作者头像
somenzz
发布2020-11-25 10:09:51
发布2020-11-25 10:09:51
4900
举报
文章被收录于专栏:Python七号Python七号

阅读本文大概需要 2~3 分钟。

昨天我分享了装饰器的使用方法,发现看的人并不多,这也正常,毕竟装饰器是一种锦上添花的东西,没有它,无法稍微麻烦点,但还是可以凑合着过的。

其实,高手和普通人就差这一点,一般人觉得学得够当下所用,也就不愿意再花时间学习了,这样也就不会再进步,也就成不了高手。

虽然我也不是高手,但我愿意持续学习,缩短与高手之间得距离。

学以致用,对于我们从事 IT 职业的,学习的东西更要使用才行,如果工作上没有需求,那么就自己创造需求,自己来实现,只有这样,才能真正的学会。否则,当时弄懂了,时间一长,全忘了,结果花了时间,白费功夫。

我很喜欢布尔值,要么是 0 要么是 1。学习也是一样,要么不学,要么就学到 100%。

下面,我们就来聊聊装饰器非常实用的应用场景。

我们写程序时都会处理异常,有些异常是需要抛出的,有些异常是可以忽略的,还有些异常通过重跑几次就解决了。假如让你写个装饰器,当被装饰的函数调用抛出指定的异常时,函数会被重新调用,直到达到指定的最大调用次数才重新抛出指定的异常,你怎么写呢?

假如有以下函数 func

代码语言:javascript
复制
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

那么:

代码语言:javascript
复制
@retry(times=3,traced_exceptions=ValueError,reraised_exception=CustomException)
def func(num):

就表示当 func 抛出 ValueError 时自动重试 3 次,如果最后抛出的是 CustomException 就抛出异常,否则就什么也不抛出。

我们还可以稍微增加点复杂度,比如:traced_exceptions 为监控的异常,可以为 None(默认)、异常类、或者一个异常类的列表,如果为 None,则监控所有的异常;如果指定了异常类,则若函数调用抛出指定的异常时,重新调用函数,直至成功返回结果或者达到最大尝试次数,此时重新抛出原异常(reraised_exception 的值为 None),或者抛出由 reraised_exception 指定的异常。

可以自己先实现下,下面给出我自己的一种实现方法:

代码语言:javascript
复制
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

给出一种运行结果:

代码语言:javascript
复制
@retry(times=3,traced_exceptions=ValueError,reraised_exception=ValueError) 

对应的结果如下:

代码语言:javascript
复制
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、代码都写好了,现在又要求增加权限验证。

有了装饰器,随你需求怎么变吧,反正我不改原有代码,就可以实现你的需求。欢迎阅读原文留言交流。

(完)

专注于有价值的分享

欢迎订阅、在看、转发

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-04-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python七号 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 其他应用场景
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档