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

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


还可以作为序列的元素:


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

__name__

__doc__

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

#/usr/bin/env Python3
#-*- encoding:UTF-8 -*-
#test.py
def add(*args): 
    """这是一个求和函数,参数可变(不知道有多少个)"""
    sum = 0
    for n in args:
        sum = sum + n
    return sum

L = [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.py
def 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 login


def 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.py

import 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.py

import functools
def 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  #返回我们的装饰器(扩展函数) 
@decoretor
def run():
    print('跑')  #装饰器作用的对象是run这个函数
    
f =run()  #因此,我们执行run函数的同时,我们的装饰器也没闲着,扩展功能将作用在run运行前后
print(run) #打印run函数对象的内存地址
print(f) #装饰器返回的是一个函数,而这个函数的返回值又是一个函数,这个函数是run函数的功能属性拷贝函数,不==run
print(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 可以用函数实现,也可以用类实现。



  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值