1、装饰器的理解
装饰器是将一个函数镶嵌在另一个函数中进行重复使用的目的,不改变其结构,增加函数的使用方式,但是不用写过多冗余的代码;
装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。
通常用到的功能:1.引入日志;2.函数执行时间统计;3.执行函数前预备处理;4.执行函数后清理功能;5.权限校验;6.缓存
2、实现原理与通用写法
咱们可以从一个简单的记录函数运行时间的简单装饰器,举一反三,推导出一个通用的装饰器写法
importtime
deftimer(func):
'''
记录方法运行时间的装饰器
:paramfunc:方法
:return:函数对象
'''
defdeco(*args,**kwargs):
startTime=time.time()
f=func(*args,**kwargs)
endTime=time.time()
msecs=(endTime-startTime)*1000
print("timeis%dms"%msecs)
returnf#如果func有返回值得话,需要在此return回去,否则,默认返回值为None,一般默认都返回
returndeco
@timer
deftest(parameter):
print("testisrunning!")
time.sleep(1)
return"Returnedvalue"#该函数有返回值,所以需要在装饰器中的deco方法中写返回值
t=test('aa')
print(t)
这是一个很简单的通用的记录时间的装饰器,从而推导出一个通用的装饰器写法:
deffunc_name(func):#自定义装饰器函数名
defdeco(*args,**kwargs):#将所有参数原封不动的进行传递
print("在这个分割线之上写函数运行前的操作")
#-----------分割线-----------
f=func(*args,**kwargs)
#-----------分割线-----------
print("在这个分割线之后,return之前,写函数运行后的操作")
returnf#如果func有返回值得话,需要在此return回去,否则,默认返回值为None,一般默认都返回
returndeco
@func_name
deftest(parameter):#8
print("testisrunning!")
time.sleep(1)
return"Returnedvalue"#该函数有返回值,所以需要在装饰器中的deco方法中写返回值
t=test('aa')
print(t)
ok 装饰器到此可以完事了,一般情况下都能满足需求了,网上看那么多原理,有点儿浪费时间,我偏向实操型,实在不喜欢啰嗦那么多,就是干。
当然在开发过程中, 我们可能会遇到一些特殊情况,比如参数问题
1、给装饰器函数代参数(通用)
2、将执行函数的参数拆分计算等(比如:1000w的数据,拆分成100份执行等)(定制)
那就按顺序来
1、写一个代参数的装饰器
deflogging(level):
defwrapper(func):
definner_wrapper(*args,**kwargs):
print("[{level}]:enterfunction{func}()".format(level=level,func=func.__name__))
returnfunc(*args,**kwargs)
returninner_wrapper
returnwrapper
@logging(level='INFO')
defsay(something):
print("say{}!".format(something))
#如果没有使用@语法,等同于
#say=logging(level='INFO')(say)
@logging(level='DEBUG')
defdo(something):
print("do{}...".format(something))
if__name__=='__main__':
say('hello')
do("mywork")
发现:就是在上面的通用的模板上又套了一层!!!,然后拿到里面的参数即可! so easy!!!
2、写一个参数拆分的装饰器,这个就稍微有点定制型了,不能像上面的一样通用了,举个 栗子:
deffunc_name(func):#自定义装饰器函数名
defdeco(*args,**kwargs):#将所有参数原封不动的进行传递
print(args[0])
f_list=[]
foriinrange(0,args[0],100000):
print(i)
f_list.append(func(i))
#f_list#这儿应该按照既定规则,继续对这个结果进行拼接,如果是写文件、入库等操作,可以不用return
returnf_list#这儿如果有返回值得话,应该是
returndeco
@func_name
deftest(parameter):#8
print("testisrunning!")
time.sleep(1)
return"Returnedvalue"#该函数有返回值,所以需要在装饰器中的deco方法中写返回值
t=test(1000000)
print(t)
可以看出来,这个的定制性稍微高点,不通用,但是我们实现了我们的需求,所以,我们最应该理解并学会的是怎么用!!!
可以看出来,这个的定制性稍微高点,不通用,但是我们实现了我们的需求,所以,我们最应该理解并学会的是怎么用!!!
下面在介绍一下基于类实现的装饰器,那问题来了,我是实战派,我并没有用类装饰器的需求,所以,当个大盗吧,以后用到了不至于瞎找了!!!
装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。在Python中一般callable对象都是函数,但也有例外。只要某个对象重载了__call__()方法,那么这个对象就是callable的。
classTest(): def__call__(self): print'callme!' t=Test() t()#callme
像__call__这样前后都带下划线的方法在Python中被称为内置方法,有时候也被称为魔法方法。重载这些魔法方法一般会改变对象的内部行为。上面这个例子就让一个类对象拥有了被调用的行为。
回到装饰器上的概念上来,装饰器要求接受一个callable对象,并返回一个callable对象(不太严谨,详见后文)。
那么用类来实现也是也可以的。我们可以让类的构造函数__init__()接受一个函数,然后重载__call__()并返回一个函数,也可以达到装饰器函数的效果。
classlogging(object):
def__init__(self,func):
self.func=func
def__call__(self,*args,**kwargs):
print"[DEBUG]:enterfunction{func}()".format(
func=self.func.__name__)
returnself.func(*args,**kwargs)
@logging
defsay(something):
print"say{}!".format(something)
带参数的类装饰器
如果需要通过类形式实现带参数的装饰器,那么会比前面的例子稍微复杂一点。那么在构造函数里接受的就不是一个函数,而是传入的参数。通过类把这些参数保存起来。
然后在重载__call__方法是就需要接受一个函数并返回一个函数。
classlogging(object):
def__init__(self,level='INFO'):
self.level=level
def__call__(self,func):#接受函数
defwrapper(*args,**kwargs):
print"[{level}]:enterfunction{func}()".format(
level=self.level,
func=func.__name__)
func(*args,**kwargs)
returnwrapper#返回函数
@logging(level='INFO')
defsay(something):
print"say{}!".format(something)