zoukankan      html  css  js  c++  java
  • python全栈开发-Day10 装饰器(闭合函数的应用场)

    一、 装饰器

      装饰器就是闭包函数的一种应用场景

      什么是闭包函数?我们再来回忆一下:

      闭包函数:

        定义在函数内部的函数,并且该函数包含对外部函数作用域(强调:对全局作用域名字的引用不算闭包)名字的引用,该函数称为闭包函数

      说到作用域我们再回忆下:

      作用域:
          全局范围:内置+全局
            全局有效,全局存活
          局部范围:局部
            局部有效,局部存活
      作用域关系是在函数定义阶段就规定死,与调用位置无关,
      也就是说,无论函数在哪儿调用,都必须回到当初定义函数时的位置找作用域关系

    一 、为何要用装饰器

    #开放封闭原则:
        #软件一旦上线后,就应该遵循开放封闭原则,对修改源代码是封闭的,对功能的扩展是开放的,也就是我们必须找到一种解决方法:
        #能够在不修改一个功能源代码以及调用方式的前提下,为其加上新功能.

    二 、什么是装饰器

    #装饰器他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象。
    #强调装饰器的原则:
        #1 、不修改被装饰对象的源代码 
        #2 、不修改被装饰对象的调用方式
    #装饰器的目标:
        #在遵循1和2的前提下,为被装饰对象添加上新功能

    三 、装饰器的使用

    #我们现在给下面函数增加一个运行时间的功能
    import time
    
    def index():
        time.sleep(3)
        print('welcome to index page')
    
    #修改一
    def index():
        start_time=time.time()
        time.sleep(3)
        print('welcome to index page')
        stop_time=time.time()
        print('run time is %s' %(stop_time-start_time))
    
    index()   #功能实现
    
    #评语:直接改源代码,这么搞被开了。。。
    #再来一位童靴,来实现功能
    #修改二
    import time
    
    def index():
        time.sleep(1)
        print('welcome to index page')
    
    start_time=time.time()
    index()
    stop_time = time.time()
    print('run time is %s' % (stop_time - start_time))
    
    #评语:有好多函数要实现这个功能,写N遍变这个代码,
    #后期维护一脸懵逼,还是被开。。。
    #再来一位童靴,功能重复实现看我用强大的函数
    #修订三:
    import time
    
    def index():
        time.sleep(3)
        print('welcome to index page')
    
    def wrapper(func):  #func=index
        start_time=time.time()
        func() #index()
        stop_time = time.time()
        print('run time is %s' % (stop_time - start_time))
    
    wrapper(index) 
    
    #评语:修改了原函数的调用方式,依然被开。。。
    #终于来了位小牛的童靴,函数的值可以返回,然后我再把重新定义index
    #修订四:
    import time
    
    def index():
        time.sleep(3)
        print('welcome to index page')
    
    def outter(func): #func=最原始的index
        # func=最原始的index
        def wrapper():
            start_time=time.time()
            func()
            stop_time=time.time()
            print(stop_time-start_time)
        return wrapper
    
    index=outter(index)  # 新的index=wrapper
    index()  #wrapper()  功能基本实现
    #评语:在原值没有返回值是没问题,但是有返回值的情况下,这么搞就会发现返回的是None
    #这次小牛牛童靴路过看到这情况,say这么搞!
    #修订五
    import time
    def index():
        time.sleep(1)
        print('welcome to index page')
        return 123     #假使这里返回123     返回值可以是任意类型 
    
    #==============下面就是装饰器
    def timmer(func):
        #func=最原始的index
        def wrapper(*args,**kwargs):     #可变长参数
            start_time=time.time()
            res=func(*args,**kwargs)  #调用最原始的index
            stop_time=time.time()
            print(stop_time-start_time)
            return res        #index()运行的返回值
        return wrapper
    
    index=timmer(index) # 新的index=wrapper
    
    print(index())    #功能已经实现,返回值123
    
    #评语:这里的装饰的功能已经实现,返回的值也得到,小牛牛不是白叫的
    #天上五彩红光,大牛童靴出现!各位童靴火速围观!
    import time
    def timmer(func):
        def wrapper(*args,**kwargs):
            start_time=time.time()
            res=func(*args,**kwargs)
            stop_time=time.time()
            print(stop_time-start_time)
            return res
        return wrapper
    
    @timmer #index=timmer(index)      装饰器的标准格式!
    def index():
        time.sleep(1)
        print('welcome to index page')
        return 123
    
    @timmer # home=timmer(home)
    def home(name):
        time.sleep(2)
        print('welcome %s to home page' %name)
    
    # index()
    #home('egon')   
    
    #评语:大牛就是大牛!大牛say:教你们绝技,不会的童靴可以照下面的模板实现:
    
    #无参装饰器模板
    def outer(func):     #outer,inner名字功能随意
        def inner(*args,**kwargs):
            res=func(*args,**kwargs)
            return res
        return inner
    
    @outer            #装饰器要在装饰函数的上方
    def duoduo():
        pass                      

    四 、装饰器语法

    #被装饰函数的正上方,单独一行
    @deco1
    @deco2
    @deco3
    def foo():
        pass
    #foo=deco1(deco2(deco3(foo)))
    #这里的思想就是最上面装饰器,装饰的下面所有的函数(deco2,deco3,foo)
    #然后deco2装饰(deco3,foo),最后deco3装饰foo
    #功能的不同,放的顺序也要注意,不然装饰的效果可能实现的就不对了!

    五、多个装饰器的使用:

    import time
    current_user={
        'username':None,
        # 'login_time':None
    }
    
    def auth(func):
        # func=index
        def wrapper(*args,**kwargs):
            if current_user['username']:   #这里是认证过的,下次就不用认证
                print('已经登陆过了')
                res=func(*args,**kwargs)
                return res
    
            uname=input('用户名>>: ').strip()
            pwd=input('密码>>: ').strip()
            if uname == 'egon' and pwd == '123':
                print('登陆成功')
                current_user['username']=uname
                res=func(*args,**kwargs)
                return res
            else:
                print('用户名或密码错误')
        return wrapper
    
    def timmer(func):
        def wrapper(*args,**kwargs):
            start_time=time.time()
            res=func(*args,**kwargs)
            stop_time=time.time()
            print(stop_time-start_time)
            return res
        return wrapper
    
    @timmer # timmer 统计的是auth+index的执行时间
    @auth             #我们只装饰index的话就要把@timmer紧跟index
    def index():
        time.sleep(1)
        print('welcome to index page')
        return 123
    
    @auth
    @timmer     #这里统计的就是home运行的时间
    def home(name):
        time.sleep(2)
        print('welcome %s to home page' %name)
    
    #index()
    #home("duoduo")

    六、有参数的装饰器的使用:

    import time
    current_user={
        'username':None,
        # 'login_time':None
    }
    
    def auth(engine):    #道理还是那个道理,在外面包了一层engine的值
        # engine='file'  #这个值外面穿什么进来就是什么
        def auth2(func):
            # func=index
            def wrapper(*args,**kwargs):
                if engine == 'file':
                    if current_user['username']:
                        print('已经登陆过了')
                        res=func(*args,**kwargs)
                        return res
    
                    uname=input('用户名>>: ').strip()
                    pwd=input('密码>>: ').strip()
                    if uname == 'egon' and pwd == '123':
                        print('登陆成功')
                        current_user['username']=uname
                        res=func(*args,**kwargs)
                        return res
                    else:
                        print('用户名或密码错误')
                elif engine == 'mysql':         #engine 值得判断情况
                    print('基于MyQL的认证')
                elif engine == 'ldap':
                    print('基于LDAP的认证')
            return wrapper
        return auth2     #这里也要返回auth2的内存地址
    
    @auth('ldap') #@auth2 #index=auth2(index) #index=wrapper
    def index():
        time.sleep(1)
        print('welcome to index page')
        return 123
    
    
    index() # wrapper()
  • 相关阅读:
    Python数据分析(一)pandas数据切片
    回归分析
    C# + ArcEngine 常用方法(不定时更新)
    安卓11配置谷歌FCM推送报错
    VUE开发之异常篇
    C#编程之“串口通讯多次接收”
    关于swift使用CocoaPods倒入三方库的framework后父类倒入子类无法继承的问题
    warning: directory not found for option“XXXXXX” 解决方案
    关于获取tableView中cell数据的处理
    怎么处理使用UINavigation(导航控制器时) UIScrollView及其子类UITableView、UICollectionView可能出现的向下偏移64Px或者顶部对齐等问题
  • 原文地址:https://www.cnblogs.com/ManyQian/p/8670190.html
Copyright © 2011-2022 走看看