Python3学习(17)--装饰器decorator

来源:互联网 发布:php工程师中国 编辑:程序博客网 时间:2024/06/05 16:19

我们知道,在Python中,函数可以当做参数来使,也可以当做函数的返回值,还可以赋给一个变量,利用变量我们也可以实现函数的功能:


还可以作为序列的元素:


这样一来,我们函数本身也是个对象,针对函数对象,Python为我们提供了其对应的属性,这里我们学习两个属性:

__name__

__doc__

下面,我们直接看下例子demo,根据demo进行注释讲解(这种方法还是比较高效容易理解的):

#/usr/bin/env Python3#-*- encoding:UTF-8 -*-#test.pydef add(*args):     """这是一个求和函数,参数可变(不知道有多少个)"""    sum = 0    for n in args:        sum = sum + n    return sumL = [1,2,3,4,5]print(add(*L)) func_name = add.__name__  # 属性:返回 函数名 print('函数名字:',func_name)func_doc  = add.__doc__   # 描述:返回 函数描述 print('函数描述:',func_doc)

我们最开始讲函数的时候,说过可变参数,就是一个变量前加一个*号,为了就是,针对不定参数的函数考虑的,如果我们要对一个整数序列进行累加求和,我们就可以构造一个L,传参的时候在L前面加一个*即可,重点不在传参,而是在打印函数的两个属性值:



okey,下面我们就开始来聊一聊今天的主题,什么是装饰器?装饰器是干嘛的,它有什么用?


一、什么是装饰器(decoretor)?

装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。


二、装饰器是干嘛的,有什么用?

抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能,也称之为扩展功能。


我们知道对象可能是一个类,也有可能是个函数,但是,本篇,我们只对函数的扩展部分进行学习,我们用装饰器来扩展函数的功能,同时不改变函数本身的定义(@functools.wraps(func)),我们用事务处理作为场景,来实现一个简单复杂例子(本篇我们先从难道易,循序渐进的吸收装饰器的定义和使用),该例子实现一个简单登陆验证功能,如果选择了验证,我们就判断用户名和密码是否正确,如果正确返回成功状态1,如果失败返回失败状态0,我们先用普通函数的思想设计我们的程序,我们将用户输入信息,是否要开启验证功能以及登录验证三者抽离,我们分成两个函数来实现,demo如下:

#/usr/bin/env Python3#-*- encoding:UTF-8 -*-#test.pydef verification(*args): #是否验证用户名和密码  True or False    def login(User,Pwd,**others):        nStatu = 0 #登录状态 0:失败  1:成功                     if args[0]:            print('开启用户和密码验证:')            if User == 'appleyk' and Pwd =='123456':                 print('登录成功!')                nStatu = 1            else:                print('登录失败!')                nStatu = 0        else:            print('不开启验证,直接登录!')            nStatu = 1        print('用户名:',User,',密码:',Pwd)            print('登录过程结束.........')        return nStatu    return logindef Test_login(User,Pwd,isTrue,**others):     f = verification(isTrue) #这个是login函数    nStatu = f(User,Pwd) #这个接收返回的登录状态值    print('登录状态: ',nStatu)Test_login('appleyk','123456',True)

Test_login用来测试用户名appleyk是否密码正确,这个函数如果想自己实现验证功能也可以,只不过所有功能用一个函数来实现,这样的工程耦合度太高,牵一发动全身,不如各个功能抽离出去,需要改动的时候,我们只针对需要的模块进行改动就行,下面我们看下这种方式的实现效果:







上面是用普通的函数封装进行Test_login函数的测试,我们发现,函数Test_login内部必须显示的一层层的调用verification函数才行,如果,我们只想让Test_login提供用户信息参数,其内部什么也不做pass,就能实现上述所有功能的话,我们要怎么做?


由此可见,我们要改进的demo,要具备以下两个特点:

(1)Test_login 函数本身不受影响,它该干嘛干嘛,不能破坏它最开始的定义--什么都不做

(2)扩展Test_login函数功能,以此到达我们的最终功能要求



这个时候,decoretor装饰器便响亮的出场了:

格式:

@de_func                  #装饰器可以有参数,无参数的话 括号可以不带

def func(*args,**others):

              pass                 #以上写法等同于  func =de_fun(func) 如果带参数就是func = de_fun(arg)(func)

func()  #执行func的同时,装饰器也会发挥其作用,将扩展的功能作用在fun运行前后


根据装饰器的格式书写,我们来改进下我们上面的demo,无非就是将功能代码这部分交给装饰器(其实就是一个作用在指定函数身上的函数,起到扩展函数功能的同时,不影响函数本身),我们来看下改后的demo:


#/usr/bin/env Python3#-*- encoding:UTF-8 -*-#decoretor.pyimport functools #导入 functools 模块    def verification(level): #是否验证用户名和密码  True or False    def decorator(func): #装饰器 func是要被装饰的 函数        @functools.wraps(func) #我们可以对func进行装饰重写的同时,保持func本身不发生改变        def login(User,Pwd,**others):#针对登录模块 进行判断用户和密码 -- 装饰开始            """Here's a doc login"""            nStatu = 0 #登录状态 0:失败  1:成功             if level:                print('开启用户和密码验证:')                if User == 'appleyk' and Pwd =='123456':                     print('登录成功!')                    nStatu = 1                else:                    print('登录失败!')                    nStatu = 0            else:                print('不开启验证,直接登录!')                nStatu = 1            print('用户名:',User,',密码:',Pwd)            func(User,Pwd,**others) #这里我们打印最开始func定义的功能,其实执行的就是Test_login,这里是什么也不输出(Pass)            print('登录过程结束.........decorator is over!') #func结束后,我们装饰的目的也就结束了            return nStatu #登录判断 返回一下 登录状态值        return login #返回这个装饰效果(登录验证函数--属于扩展功能)    return decorator #返回装饰器(包装函数,这个函数在func运行期间,对其进行了扩展)                             @verification(False) #注意 verification返回的是一个装饰器,这个装饰器对函数Test_login实现功能扩展def Test_login(User,Pwd,**others):    """Here's a doc Test_login"""    pass       n = Test_login('appleyk','123456')print("登录状态:",n)print(Test_login.__name__,',注释标记:',Test_login.__doc__)
我们先看下执行结果:



我们说过,装饰器装饰过的函数,要在装饰结束后,前后不发生结构上的改变,正如我们看到的结果,Test_login的name属性到最后依然是Test_login(可能你这会还觉得,这不本来就应该是吗,怎么是依然是,莫非,装饰后,它还可以变?),我们注意到案例demo里面有行代码是这样的:


这行代码至关重要,直接影响被装饰的函数Test_login,我们将其注释掉


然后,我们,再运行demo看看效果:



上面的例子如果半懂的话,证明你对这种装饰的过程已经有了认识,我们反着来,弄一个简单的装饰器玩玩:

#/usr/bin/env Python3#-*- encoding:UTF-8 -*-#test.pyimport functoolsdef decoretor(func):    @functools.wraps(func)#注意,我们最后还是要验证run还是不是run    def my_run(): #扩展run,在跑之前喊 预备,跑完之后,告知比赛结束        print('预备................')        f = func #我们最后返回这个func ,实际上也可以不这样写,直接 return func就行,这里为了展示效果        func() #这里我们开始 跑,实际上执行的是函数run        print('比赛结束............')        return f #返回 f -- func函数的副本,这里返回的是run函数的副本,功能一模一样,但是但是函数对象的地址不一样    return my_run  #返回我们的装饰器(扩展函数) @decoretordef run():    print('跑')  #装饰器作用的对象是run这个函数    f =run()  #因此,我们执行run函数的同时,我们的装饰器也没闲着,扩展功能将作用在run运行前后print(run) #打印run函数对象的内存地址print(f) #装饰器返回的是一个函数,而这个函数的返回值又是一个函数,这个函数是run函数的功能属性拷贝函数,不==runprint(f.__name__)  #无论有没有@functools.wraps(func)限制,这个f始终是run,也就是run的副本print(run.__name__)#run是不是run还要看@functools.wraps(func)这行代码f() #我们运行一下run函数的副本

针对以上以上demo,我们分两种情况来执行输出,一种就是有@functools.wraps(func)修饰,一种是无

A、@functools.wraps(func)



A、#@functools.wraps(func)



装饰器总结:

通过使用装饰器,我们可以很轻松的实现函数的扩展功能,从而不改变函数本身的性质,而装饰器又非常的灵活,稍加设计即可写出来很棒很强大又很有用的功能,来满足我们对于函数的增强。Python 的 decorator 可以用函数实现,也可以用类实现。