python 多层装饰器分析

发布时间:2018-04-04 00:11:50编辑:admin阅读(4397)

    装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是1函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。


    一层装饰器

    应用场景最多的就是登录判断

    举个例子

    默认情况下,登录状态是False。现在有一个需求,需要执行func1执行时,必须要登录才行。

    需要增加一个装饰器用来判断

    #用户状态(全局变量)
    login_state = {'username':None,'status':False}
    
    #装饰器模板
    def first(f):
        def inner(*args,**kwargs):
            '''被装饰之前'''
            ret = f(*args, **kwargs)
            '''被装饰之后'''
            return ret
        return inner
    
    #测试函数
    def func1(*args,**kwargs):
        print(args,kwargs)
        return 666
    #执行函数
    print(func1())

    执行输出:

    () {}

    666


    代码如下:

    #用户状态(全局变量)
    login_state = {'username':None,'status':False}
    
    #假设用户名存在文件或者数据库中
    info = {'username':'xiao','password':'123'}
    #失败次数
    failures = 1
    #最大登录次数
    maximum = 3
    
    #装饰器模板
    def first(f):
        def inner(*args,**kwargs):
            '''被装饰之前'''
            if login_state['status']:
                ret = f(*args, **kwargs)
            else:
                print('您还未登录,请先登录账户!')
                #执行登录程序
                result = login()
                #判断结果
                if result:
                    print('登录成功')
                else:
                    print('登录失败')
                ret = f(*args, **kwargs)
    
            #执行被装饰的函数
            return ret
            '''被装饰之后'''
    
        return inner
    
    #登录判断逻辑
    def login():
        global failures
        while failures <= maximum:
            username = input('请输入用户: ').strip()
            password = input('请输入密码: ').strip()
            #判断用户名和密码是否一致
            if username == info['username'] and password == info['password']:
                # 修改全局变量,这里不用global,因为它是可变类型
                login_state['username'] = username
                login_state['status'] = True
                result = True
                break
            else:
                print('您输入的用户或者密码错误,请重新输入,还有%s次机会' % (maximum - failures))
                failures += 1
                result = False
        return result
    
    #测试函数
    @first
    def func1(*args,**kwargs):
        print(args,kwargs)
        return 666
    
    #执行函数
    print(func1())

    执行输出:

    blob.png


    现在又有一个需求,执行watch_movie函数时,必须是VIP用户才可以。

    增加二层装饰器

    #!/usr/bin/env python
    # coding: utf-8
    __author__ = 'www.py3study.com'
    
    #用户状态(全局变量)
    login_state = {'username':None,'status':False}
    
    #假设用户名存在文件或者数据库中
    info = {'username':'xiao','password':'123','level':None}
    #失败次数
    failures = 1
    #最大登录次数
    maximum = 3
    
    #第一层装饰器,判断登录
    def first(f):
        def inner(*args,**kwargs):
            '''被装饰之前'''
            if login_state['status']:
                ret = f(*args, **kwargs)
            else:
                print("\033[41;1m您还未登录,请先登录账户!\033[0m")
                #执行登录程序
                result = login()
                #判断结果
                if result:
                    print('登录成功')
                else:
                    print("\033[41;1m登录失败!\033[0m")
    
                ret = f(*args, **kwargs)
    
            #执行被装饰的函数
            return ret
            '''被装饰之后'''
    
        return inner
    
    #第二层装饰器,判断VIP
    def second(f):
        def inner(*args,**kwargs):
            '''被装饰之前'''
            #判断电影名后缀是否包含VIP
            if args[0].endswith('VIP'):
                if info['level'] == None:
                    return "\033[41;1m该影片只对VIP用户开放!\033[0m"
                else:
                    ret = f(*args, **kwargs)
            else:
                #免费电影
                ret = f(*args, **kwargs)
    
            return ret
            '''被装饰之后'''
            #return ret
        return inner
    
    #登录判断逻辑
    def login():
        global failures
        while failures <= maximum:
            username = input('请输入用户: ').strip()
            password = input('请输入密码: ').strip()
            #判断用户名和密码是否一致
            if username == info['username'] and password == info['password']:
                # 修改全局变量,这里不用global,因为它是可变类型
                login_state['username'] = username
                login_state['status'] = True
                #设置标记
                result = True
                break
            else:
                print("\033[41;1m您输入的用户或者密码错误,请重新输入,还有%s次机会!\033[0m" % (maximum - failures))
                failures += 1
                result = False
    
        return result
    
    #看电影函数
    @first
    @second
    def watch_movie(name):
        if name.strip() == '':
            return '电影名不能为空'
        return '正在播放影片 {}'.format(name)
    
    #首页
    def index():
        movie_list = {
            #序号:电影名
            1: '黑暗塔',
            2: '环太平洋 VIP',
            3: '变形金刚5:最后的骑士 VIP',
            4: '退出',
        }
    
        while True:
            #循环列表
            for k, v in movie_list.items():
                print('{}\t{}'.format(k, v))
            choice_num = input('请选择输入的序号:').strip()
            if choice_num.isdigit():
                choice_num = int(choice_num)
                #判断数字范围
                if 0 < choice_num < len(movie_list):
                    #执行看电影函数
                    print(watch_movie(movie_list[choice_num]))
                elif choice_num == len(movie_list):
                    break
                else:
                    print("\033[41;1m请输入范围内的序号!\033[0m")
            else:
                print("\033[41;1m您输入的有非法字符,请重新输入!\033[0m")
    
    if __name__ == "__main__":
        index()

    执行输出:

    jd1.gif

    修改全局变量

    info = {'username':'xiao','password':'123','level':'VIP'}

    再次登录,就可以看VIP电影了


    装饰器执行顺序是这样的

    blob.png

    上述代码的二层装饰器,都是在被装饰之前操作的

    first判断是否登录

    second判断用户等级


    二层以上装饰器,也都是按照上图的循环来的,多套了几层而已!


关键字