装饰器简单来说,就是现有的对象,在不修改源代码和调用方式的情况,对现有的对象添加新的功能,比如插入日志,权限校验之类的。
为什么要用装饰器?
1、现在有这么一个对象,我要给这个对象添加一个打印日志的功能
1deffoo():2print("I'mfoofunction")2、那么可以这样改
1deffoo():2print("I'mfoofunction")3print("logging")但是如果有10个对象,是不是要在10处加上打印日志的代码?那如果是50个,100个呢?这样会有大量重复的代码出现
3、那还可以这样改,把foo当作一个参数传给日志功能不就好了么
1deffoo():2print("I'mfoofunction")34deflogging(func):5print("logging")6func()78logging(foo)这样会有一个情况就是,修改了调用方式,假如这段代码已经在生产环境中运行了,而且有很多地方都引用这段代码,不可能把所有已经引用的地方全都修改了吧
这时候就需要用到装饰器了,不用修改原来对象的代码和调用方式,添加新的功能,还可以避免写重复的代码
函数可以赋值给变量
例如
1deffoo():2print("I'mfoofunction")34f=foo5f()67#输出结果8I'mfoofunction
函数名后面有小括号和没有小括号的区别
1deffoo():2print("I'mfoofunction")34print(foo)#没有小括号表示函数的内存地址5print('---我---是---分---割---线---')6print(foo())#有小括号表示函数的执行结果78#输出结果9
为什么要说这个函数可以赋值和有没有小括号的问题呢,因为下面会用到这个
没有参数的对象进行装饰
1、还是这个例子,为其添加日志功能
1deffoo():2print("I'mfoofunction")2、下面看一下两个例子
(1)语法糖的写法
1deflogging(func):#12defdeco():#33print("logging")#64func()#75returndeco#467@logging#28deffoo():9print("I'mfoofunction")#81011foo()#51213#输出结果14logging15I'mfoofunction(注:#1#2#3....#8其表示的意思是在调试模式下的执行顺序)(2)重新赋值的写法
1deflogging(func):#12defdeco():#43print("logging")#74func()#85returndeco#567deffoo():#28print("I'mfoofunction")#9910foo=logging(foo)#311foo()#61213#输出结果14logging15I'mfoofunction(注:#1#2#3....#9其表示的意思是在调试模式下的执行顺序)
这两个例子是等价关系,语法糖@logging的写法,相当于隐式的做了foo=logging(foo)
logging(foo)返回的是deco这个函数的内存地址,被重新赋值给了foo这个函数名,所以现在foo=deco
当执行foo()的时候,其实就是deco()
需要注意的是foo和foo()这两个的区别,foo是表示一个内存地址,而foo()表示的是执行结果
有参数的对象进行装饰
1deflogging(func):2defdeco(*args,**kwargs):#*args表示可以接收任意个位置参数,**kwargs表示可以接收任意个关键字参数,位置参数*args要放在关键字参数**kwargs的前面3print("logging")4func(*args,**kwargs)5returndeco67@logging8deffoo():9print("Mynameisfoofunction")1011@logging12defbar(a,b):13print("Mynameis%s%s"%(a,b))1415@logging16deften(x,y):17print("Mynameis%s%s"%(x,y))181920foo('foo')21print('---我---是---分---割---线---')22bar('bar','function')23print('---我---是---分---割---线---')24ten('ten',y='function')2526#输出结果27logging28Mynameisfoofunction29---我---是---分---割---线---30logging31Mynameisbarfunction32---我---是---分---割---线---33logging34Mynameistenfunction可适用于不带参数的对象、带参数的对象,以及关键字参数的对象的装饰