• 装饰器


    1、什么是装饰器

      器指的是工具,可以定义成成函数

      装饰指的是为其他事物添加额外的东西点缀

      合到一起的解释:装饰器指的定义一个函数,该函数是用来为其他函数添加额外的功能

    2、为何要用装饰器

      开放封闭原则

        开放:指的是对拓展功能是开放的

        封闭:指的是对修改源代码是封闭的

      装饰器就是在不修改被装饰器对象源代码以及调用方式的前提下为被装饰对象添加新功能

    3、如何用

      1.无参装饰器

      需求:在不修改index函数的源代码以及调用方式的前提下为其添加统计运行时间的功能

      让我们一步步的得到装饰器

    def index(x,y):
        time.sleep(3)
        print('index %s %s' %(x,y))
    
    index(111,222)

       解决方案一失败:没有修改被装饰对象的调用方式,但是修改了其源代码

    import time
    def index(x,y):
        start=time.time()
        time.sleep(3)
        print('index %s %s' %(x,y))
        stop = time.time()
        print(stop - start)
    index(111,222)

      解决方案二失败:没有修改被装饰对象的调用方式,也没有修改了其源代码,并且加上了新功能,但是代码冗余

    import time
    def index(x,y):
        time.sleep(3)
        print('index %s %s' %(x,y))
    start=time.time()
    index(111,222)
    stop=time.time()
    print(stop - start)

      解决方案三失败:解决了方案二代码冗余问题,但带来一个新问题即函数的调用方式改变了

    import time
    def index(x,y):
        time.sleep(3)
        print('index %s %s' %(x,y))
    def wrapper():
        start=time.time()
        index(111,222)
        stop=time.time()
        print(stop - start)
    wrapper()

      大方向:如何在方案三的基础上不改变函数的调用方式

      方案三优化一:将index的参数写活了

    import time
    def index(x,y):
        time.sleep(3)
        print('index %s %s ' %(x,y))
    def wrapper(*args,**kwargs):
        start=time.time()
        index(*args,**kwargs) 
        stop=time.time()
        print(stop - start)
    
    wrapper(3333,4444)

      方案三优化二:在优化一的基础上把被装饰对象写活了,原来只能装饰index

    import time
    def index(x,y):
        time.sleep(3)
        print('index %s %s %s' %(x,y))
    def home(name):
        time.sleep(2)
        print('welcome %s to home page' %name)
    def outter(func):
        def wrapper(*args,**kwargs):
            start=time.time()
            func(*args,**kwargs) 
            stop=time.time()
            print(stop - start)
        return wrapper
    index=outter(index) # index=wrapper的内存地址
    home=outter(home) # home=wrapper的内存地址
    home('egon')

      方案三优化三:将wrapper做的跟被装饰对象一模一样,以假乱真

    import time
    def home(name):
        time.sleep(2)
        print('welcome %s to home'%name)
        return 123    
        
    def index(x,y):
        time.sleep(2)
        print('index %s %s' % (x, y))
    
    def outtor(func):
        def wrapper(*args,**kwargs):
            start = time.time()
            res = func(*args,**kwargs) #返回值也要一样
            stop = time.time()
            print(stop-start)
            return res
        return wrapper
    
    index = outtor(index)# 偷梁换柱:index这个名字指向的wrapper函数的内存地址
    index(1,2)
    home = outtor(home)# 偷梁换柱:home这个名字指向的wrapper函数的内存地址
    res = home('jjj')
    print(res)

      最终结果:

      语法糖:在被装饰对象正上方的单独一行写@装饰器名字(让你开心的语法

    import time
    @outtor
    def home(name):
        time.sleep(2)
        print('welcome %s to home'%name)
        return 123    
    @outtor    
    def index(x,y):
        time.sleep(2)
        print('index %s %s' % (x, y))
    
    def outtor(func):
        def wrapper(*args,**kwargs):
            start = time.time()
            res = func(*args,**kwargs) #返回值也要一样
            stop = time.time()
            print(stop-start)
            return res
        return wrapper
    
    index(1,2)
    res = home('jjj')
    print(res)

       2.有参装饰器

          由于语法糖@的限制,outter函数只能有一个参数,并且该参数只用来接收被装饰对象的内存地址,而wrapper的参数同样不能修改

    def auth(db_type):
        def deco(func):
            def wrapper(*args, **kwargs):
                name = input('your name>>>: ').strip()
                pwd = input('your password>>>: ').strip()
    
                if db_type == 'file':
                    print('基于文件的验证')
                    if name == 'egon' and pwd == '123':
                        res = func(*args, **kwargs)  
                        return res
                    else:
                        print('user or password error')
                elif db_type == 'mysql':
                    print('基于mysql的验证')
                elif db_type == 'ldap':
                    print('基于ldap的验证')
                else:
                    print('不支持该db_type')
            return wrapper
        return deco
    
    
    @auth(db_type='file')  # @deco # index=deco(index) # index=wrapper
    def index(x, y):
        print('index->>%s:%s' % (x, y))
    
    @auth(db_type='mysql')  # @deco # home=deco(home) # home=wrapper
    def home(name):
        print('home->>%s' % name)
    
    
    @auth(db_type='ldap')  # 账号密码的来源是ldap
    def transfer():
        print('transfer')
    # 有参装饰器模板
    def 有参装饰器(x,y,z):
        def outter(func):
            def wrapper(*args, **kwargs):
                res = func(*args, **kwargs)
                return res
            return wrapper
        return outter
    
    @有参装饰器(1,y=2,z=3)
    def 被装饰对象():
        pass
    有参装饰器模板

      3.装饰器的补充

        通过from functools import wraps并在wraps装饰器上方加上@wraps(func)可以让wrapper做的跟原函数一样才行

    叠加多个装饰器的加载、运行分析(了解***)

    def deco1(func1): # func1 = wrapper2的内存地址
        def wrapper1(*args,**kwargs):
            print('正在运行===>deco1.wrapper1')
            res1=func1(*args,**kwargs)
            return res1
        return wrapper1
    
    def deco2(func2): # func2 = wrapper3的内存地址
        def wrapper2(*args,**kwargs):
            print('正在运行===>deco2.wrapper2')
            res2=func2(*args,**kwargs)
            return res2
        return wrapper2
    
    def deco3(x):
        def outter3(func3): # func3=被装饰对象index函数的内存地址
            def wrapper3(*args,**kwargs):
                print('正在运行===>deco3.outter3.wrapper3')
                res3=func3(*args,**kwargs)
                return res3
            return wrapper3
        return outter3
    
    
    # 加载顺序自下而上(了解)
    @deco1      # index=deco1(wrapper2的内存地址)        ===> index=wrapper1的内存地址
    @deco2      # index=deco2(wrapper3的内存地址)        ===> index=wrapper2的内存地址
    @deco3(111) # ===>@outter3===> index=outter3(index) ===> index=wrapper3的内存地址
    def index(x,y):
        print('from index %s:%s' %(x,y))
    
    # 执行顺序自上而下的,即wraper1-》wrapper2-》wrapper3
    index(1,2) # wrapper1(1,2)
  • 相关阅读:
    vue项目打包后css背景图路径不对的问题
    Vue项目图片剪切上传——vue-cropper的使用
    Vue项目使用AES做加密
    VUE滚动条插件——vue-happy-scroll
    自实现jQuery版分页插件
    HTML5中的Web Notification桌面通知
    img标签实现和背景图一样的显示效果——object-fit和object-position
    自实现PC端jQuery版轮播图
    web前端几个小知识点笔记
    限制可编辑div只能输入纯文本
  • 原文地址:https://www.cnblogs.com/bk134/p/12552983.html
Copyright © 2020-2023  润新知