我们知道,在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)
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函数的测试,我们发现,函数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 可以用函数实现,也可以用类实现。