zoukankan      html  css  js  c++  java
  • 装饰器

    软件开发原则之一

    开放封闭原则:不改变调用方式与源代码的基础上增加新功能
      封闭:不能修改被装饰对象(函数)的源代码、调用方式
      开放:增加、扩展新的功能效果

    装饰器:就是闭包的一种应用
        把要被装饰的函数作为外层函数的参数通过闭包操作后返回一个替代版函数
        被装饰的函数:fn
        外层函数:outer(func)  outer(fn) => func = fn
        替代版函数: return inner: 原功能+新功能
    def fn():
        print("原有功能")
    
    # 装饰器
    def outer(tag):
        def inner():
            tag()
            print(新增功能")
        return inner
    fn = outer(fn)              
                  
    fn()

    开放封闭原则的实现
    def vase():
        print('插花')    #原功能:花瓶的作用
    vase()
    
    
    
    def vase():
        print('插花')
        print('绘画:进行观赏')   # 1)增加观赏功能:不满足开放封闭原则,修改了源代码
    vase()
    
    
    
    def wrap(fn):
        vase()
        print('绘画:进行观赏')  # 2)增加观赏功能:不满足开放封闭原则,修改了调用方式
    wrap(vase)
    
    
    def fn():
        vase()
        print('绘画:进行观赏')  # 3)满足了开放封闭原则,但是出现了函数调用的死循环
    vase = fn
    vase()
    
    
    # 4)下方的函数嵌套结构就是装饰器
    def wrap(tag):
        def fn():
            tag()      # 原有的vase
            print('绘画:进行观赏')
        return fn  # 拓展功能后的vase
    vase = wrap(vase)  # 将拓展功能后的功能函数重新赋值给vase
    
    
    vase()  # 功能拓展了,且调用方式不变
    
    
    -----------------------------------------------------------------------
    # 了解:满足开放封闭原则,且可以达到装饰器的作用:拓展功能
    def vase():
        print('插花')
    tag = vase  # 暴露在全局:很容易被修改掉
    def fn():
        tag()
        print('绘画:进行观赏')
    vase = fn
    vase()
    
    
    def wrap(tag):
        def fn():
            tag()
            print('绘画:进行观赏')
        return fn
    vase = wrap(vase)
    vase()
    
    
    
    ---------------------------------------------------------------------
    # 1.0版本
    def fn():
        print('fn run')
    fn()
    
    # 2.0版本
    def fn():
        print('fn run0')
        print('fn run1')
        print('fn run2')
    fn()
    
    # 修改了源代码,没有更改调用方式,对外调用方式还是原来的,但功能要有所增加(开放)
    def fn():
        print('fn run0')
        print('fn run')
        print('fn run2')
    fn()
    
    # 更改了调用方式,没有修改原功能代码(封闭)
    def wrap(fn):
        print('fn run0')
        fn()
        print('fn run2')
    wrap(fn)
    View Code
     

    @语法糖: @外层函数

    一个函数可以被任意一个相关装饰器装饰,也可以被任意几个装饰器装饰
    注意:装饰的顺序会影响新增功能的执行顺序

    def outer(f):
        def inner():
            f()
            print("新增功能1")
        return inner
                  
    def wrap(f):
        def inner():
            f()
            print("新增功能2")
        return inner              
    
    @wrap  # 被装饰的顺序决定了新增功能的执行顺序
    @outer  # <==> fn = outer(fn): inner      
    def fn():
        print("原有功能")
    示例:花瓶新增功能
    def outer(fn):
        def inner():
            fn()
            print("绘画:进行观赏")
        return inner
    
    
    def wrap(fn):
        def inner():
            fn()
            print('摆放功能')
        return inner
    
    
    # 语法糖 | 笑笑语法
    @wrap
    @outer  # <==> vase = outer(vase)
    def vase():
        print('插花')
    # vase = outer(vase)
    
    vase()
    View Code

    叠加多个装饰器

    import time
    
    def timmer(func):  #func=wrapper2
        def wrapper1(*args,**kwargs):
            start=time.time()
            res=func(*args,**kwargs)  #res=wrapper2(*args,**kwargs)
            stop=time.time()
            print('run time is %s' %(stop - start))
            return res
        return wrapper1
    
    def auth(func): #func=最原始的那个index的内存地址
        def wrapper2(*args,**kwargs):
            inp_user = input('please input your username: ').strip()
            inp_pwd = input('please input your password: ').strip()
            if inp_user == 'egon' and inp_pwd == '123':
                print('login successfull')
                res=func(*args,**kwargs) # 调用最原始的那个/也就是被装饰的那个函数
                return res
            else:
                print('username or password error')
        return wrapper2
    
    # 解释@语法的时候是自下而上运行
    # 而执行装饰器内的那个wrapper函数时的是自上而下
    @timmer # index=timmer(wrapper2) #index=wrapper1
    @auth # index=auth(最原始的那个index的内存地址) #index=wrapper2
    def index():
        print('welcome to index page')
        time.sleep(3)
    
    index() #wrapper1()
    #--------------------------------1------------------------------------
    import time
    
    def timmer(func):
        print('timmer')
        def wrapper1(*args,**kwargs):
            start=time.time()
            res=func(*args,**kwargs)  #res=wrapper2(*args,**kwargs)
            stop=time.time()
            print('run time is %s' %(stop - start))
            return res
        return wrapper1
    
    def auth(func):
        print('auth')
        def wrapper2(*args,**kwargs):
            inp_user = input('please input your username: ').strip()
            inp_pwd = input('please input your password: ').strip()
            if inp_user == 'egon' and inp_pwd == '123':
                print('login successfull')
                res=func(*args,**kwargs) # 调用最原始的那个/也就是被装饰的那个函数
                return res
            else:
                print('username or password error')
        return wrapper2
    
    
    @auth   # index=auth(wrapper1) #index=wrapper2
    @timmer #timmer(最原始的index)返回wrapper1
    def index():
        print('welcome to index page')
        time.sleep(3)
    
    index() #wrapper2()
    
    
    
    
    #------------------------------------2--------------------------------------------
    import time
    
    
    
    def outter1(func1): #func1=wrapper2
        print('outter1')
        def wrapper1(*args,**kwargs):
            print('wrapper1')
            res1=func1(*args,**kwargs) #res1=wrapper2(*args,**kwargs)
            return res1
        return wrapper1
    
    def outter2(func2): #func2=最原始的那个index的内存地址
        print('outter2')
        def wrapper2(*args,**kwargs):
            print('wrapper2')
            res2=func2(*args,**kwargs)
            return res2
        return wrapper2
    
    
    @outter1 # index=outter1(wrapper2) #index=wrapper1
    @outter2 #outter2(最原始的那个index的内存地址) ===> wrapper2
    def index():
        print('welcome to index page')
        time.sleep(3)
    
    index()  #wrapper1()
    View Code

     

    有参有返的函数被装饰

    def outer(func):
        def inner(*args, **kwargs):
            print("新增功能1")
            result = func(*args, **kwargs)
            print("新增功能2")
            return result
        return inner
    
    @outer
    def func(*args, **kwargs):
        print("原有功能")
        return "原有结果"

    有参装饰器

    def wrap(arg):
        def outer(func):
            def inner(*args, **kwargs):
                print("新增功能1")
                result = func(*args, **kwargs)
                print("新增功能2")
                return result
            return inner
    
    @wrap("装饰器参数")
    def func(*args, **kwargs):
        print("原有功能")
        return "原有结果"

     示例:

    def check_usr(fn):  # fn, login, inner:不同状态下的login,所以参数是统一的
        def inner(usr, pwd):
            # 在原功能上添加新功能
            if not (len(usr) >= 3 and usr.isalpha()):
                print('账号验证失败')
                return False
            
            # 原有功能
            result = fn(usr, pwd)
            
            # 在原功能下添加新功能
            # ...
            
            return result
           return inner
    
    
    @check_usr
    def login(usr, pwd):
        if usr == 'abc' and pwd =='123qwe':
            print('登录成功')
            return True
        print('登录失败')
        return False

    总结:
    1.login有参数,所以inner与fn都有相同参数
    2.login有返回值,所以inner与fn都有返回值
    inner(usr, pwd):
        res = fn(usr, pwd)  # 原login的返回值
        return res
    
    
    login = check_usr(login) = inner
    
    res = login('abc', '123qwe')  # inner的返回值
    import time
    
    current_user={'username':None}
    
    # 补充:所有的数据类型的值自带布尔值,可以直接当作条件去用,只需要记住布尔值为假的那一些值即可(0,空,None)
    
    
    def login(engine='file'): #engine='mysql'
        def auth(func): #func=最原始那个index的内存地址
            def wrapper(*args,**kwargs):
                if current_user['username']:
                    print('已经登录过了,无需再次登陆')
                    res=func(*args,**kwargs)
                    return res
    
                if engine == 'file':
                    inp_user = input('please input your username: ').strip()
                    inp_pwd = input('please input your password: ').strip()
                    if inp_user == 'egon' and inp_pwd == '123':
                        print('login successfull')
                        current_user['username']=inp_user # 在登陆成功之后立刻记录登录状态
                        res=func(*args,**kwargs) # res=最原始那个index的内存地址(*args,**kwargs)
                        return res
                    else:
                        print('username or password error')
                elif engine == 'mysql':
                    print('基于mysql的认证机制')
                elif engine == 'ldap':
                    print('基于ldap的认证机制')
                else:
                    print('无法识别的认证源')
            return wrapper
        return auth
    
    @login('file')  #@auth # index=auth(最原始那个index的内存地址) #index=wrapper
    def index():
        print('welcome to index page')
        time.sleep(3)
    
    @login('file')
    def home(name):
        print('welcome %s to home page' %name)
        time.sleep(2)
        return 123
    
    
    index() #wrapper()
    res=home('egon')
    print(res)
    View Code
    # 有参装饰器的模板
    def outter1(x,y,z):
        def outter2(func):
            def wrapper(*args,**kwargs):
                res=func(*args,**kwargs)
                return res
            return wrapper
        return outter2
    
    # 无参装饰器的模板
    def outter(func):
        def wrapper(*args,**kwargs):
            res=func(*args,**kwargs)
            return res
        return wrapper

    装饰器的最终写法

    def wrap(fn):
        def inner(*args, **kwargs):
            print('前增功能')
            result = fn(*args, **kwargs)
            print('后增功能')
            return result
        return inner
    
    @wrap
    def fn1():
        print('fn1的原有功能')
    @wrap
    def fn2(a, b):
        print('fn2的原有功能')
    @wrap   
    def fn3():
        print('fn3的原有功能')
        return True
    @wrap
    def fn4(a, *, x):
        print('fn4的原有功能')
        return True
    
    fn1()
    fn2(10, 20)
    fn3()
    fn4(10, x=20)
    def outer(input_color):
        def wrap(fn):
            if input_color == 'red':
                info = '33[36;41mnew action33[0m'
            else:
                info = 'yellow:new action'
    
            def inner(*args, **kwargs):
                pass
                result = fn(*args, **kwargs)
                print(info)
                return result
            return inner
        return wrap  # outer(color) => wrap
    
    
    color = input('color: ')
    @outer(color)  # @outer(color) ==> @wrap  # func => inner
    def func():
        print('func run')
    
    func()
    带参装饰器(了解)

    登录认证功能

    is_login = False  # 登录状态
    
    def login():
        usr = input('usr: ')
        if not (len(usr) >= 3 and usr.isalpha()):
            print('账号验证失败')
            return False
        pwd = input('pwd: ')
        if usr == 'abc' and pwd =='123qwe':
            print('登录成功')
            is_login = True
        else:
            print('登录失败')
            is_login = False
    
    
    # 完成一个登录状态校验的装饰器
    def check_login(fn):
        def inner(*args, **kwargs):
            # 查看个人主页或销售功能前:如果没有登录先登录,反之可以进入其功能
            if is_login != True:
                print('你未登录')
                login()
            # 查看个人主页或销售
            result = fn(*args, **kwargs)
            return result
        return inner
    
    # 查看个人主页功能
    @check_login
    def home():
        print('个人主页')
    
    # 销售功能
    @check_login
    def sell():
        print('可以销售')
    
    home()
    from functools import wraps
    def outer(func):
        @wraps(func)
        def inner(*args, **kwargs):
            '''装饰器文档注释'''
            func(*args, **kwargs)
        return inner
    
    @outer
    def func(*args, **kwargs):
        '''原有文档注释'''
        print("原有功能")
    wraps修改函数文档注释
  • 相关阅读:
    滤镜
    android 判断网络是否断开
    U5
    android 界面跳转
    光影魔术手 图片压缩
    服务器绑定 独立ip
    Java 开源 SOCKET 框架
    spring aop ehcache
    呼叫中心
    华丽字体
  • 原文地址:https://www.cnblogs.com/zhouyongv5/p/10643101.html
Copyright © 2011-2022 走看看