之前总觉得装饰器好难理解,静下心来研究了一番,其实也还算简单。
我觉得他的作用(好处):
1、不改变函数调用方式的情况下,增加函数功能,例如日志记录、认证、授权检查等。
2、代码复用,装饰器本身也是一个函数,一个功能多次调用
3、代码看起来更牛逼!
装饰器最基本使用方法
1、我们有一个最简单的打印函数,想要在不改变函数调用的情况下增加记录日志的功能:
def func_print():
print('这就是个简单的打印函数,打印这句话')
if __name__=="__main__":
func_print()
2、我们需要构造一个装饰器函数来增加日志功能
我的理解:
装饰器就是函数的多层嵌套(最基本的使用方法就是两层函数嵌套),装饰器在装饰函数时,外层函数先被调用,外层函数定义了一个内层函数,装饰器外层函数return值为内层函数的函数名(C语言里面的指针比较好理解,就是内层程序的执行入口)
实现效果:装饰器内层函数在原函数的基础上添加了新的功能,通过外层函数的调用,返回内层函数的执行入口,调用原函数的时候实际上是调用了名字一样的装饰器内层函数
@Decorator_Logging的作用== func_print=Decorator_Logging(func_print)
Decorator_Logging(func_print)的返回值是内层函数的执行入口(函数名)。
举个例子:
#构造装饰器函数,传入形参为函数
def Decorator_Logging(func):
#函数里面定义函数,用来作为上一层函数的返回值,可以理解为:调用外层函数的时候,返回内层函数的‘指针’
def wrapper_func():
print('save log to some file:***.log ')
#原函数不变,将在内层函数被调用的时候执行
func()
print('log saved to file:***.log ')
#返回内层函数执行入口
return wrapper_func
#装饰器的调用方式,@+函数名 ,作用上= func_print=Decorator_Logging(func_print)
@Decorator_Logging
def func_print():
print('这就是个简单的打印函数,打印这句话')
if __name__=="__main__":
func_print()
输出:
save log to some file:***.log
这就是个简单的打印函数,打印这句话
log saved to file:***.log
看似主程序里面调用的还是原来的func_print(),实际上早已被装饰器偷天换日。(这里说的比较简单,想更深层次理解装饰器,建议搜索下‘Python 闭包’)
装饰器传入参数
以上只是装饰器最简单的应用,当你写的函数需要传参数的时候怎么使用装饰器?
最简单的理解,原函数有参数,最终执行的时候执行的是装饰器的内层函数,那么应该保持原函数和装饰器的内层参数一致。
def Decorator_Logging(func):
#传入参数
def wrapper_func(some_str):
print('save log to some file:***.log ')
func(some_str)
print('log saved to file:***.log ')
return wrapper_func
@Decorator_Logging
def func_print(some_str):
print('这就是个简单的打印函数,打印这句话%s'%(some_str))
if __name__=="__main__":
func_print('我爱你')
输出:
save log to some file:***.log
这就是个简单的打印函数,打印这句话我爱你
log saved to file:***.log
不定参数
装饰器另一个功能也是为了复用,如果有其它函数也需要用日志的装饰器,但是函数和现在的函数参数数量内容都不一样该怎么办?
使用不定参数解决,装饰器返回值为函数入口,此时内层函数并未被调用,内层函数的参数也未被确定,为了扩展,我们就让它支持任意形式参数
args和*kwargs的用法就不再单独介绍了,直接上实例:
def Decorator_Logging(func):
#传入不定参数
def wrapper_func(*args,**kwargs):
print('save log to some file:***.log ')
#传入不定参数
func(*args,**kwargs)
print('log saved to file:***.log ')
return wrapper_func
@Decorator_Logging
def func_print(some_str):
print('这就是个简单的打印函数,打印这句话%s'%(some_str))
@Decorator_Logging
def func_print2(some_str1,some_str2):
print('这是第二个简单的打印函数,打印这句话%s,%s'%(some_str1,some_str2))
if __name__=="__main__":
func_print('我爱你')
func_print2('我爱你','那么爱你!')
输出:
save log to some file:***.log
这就是个简单的打印函数,打印这句话我爱你
log saved to file:***.log
save log to some file:***.log
这是第二个简单的打印函数,打印这句话我爱你,那么爱你!
log saved to file:***.log
返回值的处理
如果定义的函数带有返回值该如何处理?
一样的思考,原函数有返回值,让装饰器内层函数也有返回值不就可以了?!!!so easy!!!
(未在函数中用return明确指出返回值的,函数默认返回None)
上示例:
#构造装饰器函数,传入形参为函数
def Decorator_Logging(func):
#传入不定参数
def wrapper_func(*args,**kwargs):
print('save log to some file:***.log ')
#传入不定参数
func_result=func(*args,**kwargs)
print('log saved to file:***.log ')
#内层函数的返回值
return func_result
return wrapper_func
@Decorator_Logging
def func_print(some_str):
return ('这就是个简单的打印函数,打印这句话%s'%(some_str))
@Decorator_Logging
def func_print2(some_str1,some_str2):
return ('这是第二个简单的打印函数,打印这句话%s,%s'%(some_str1,some_str2))
if __name__=="__main__":
print(func_print('我爱你'))
print(func_print2('我爱你','那么爱你!'))
输出:
save log to some file:***.log
log saved to file:***.log
这就是个简单的打印函数,打印这句话我爱你
save log to some file:***.log
log saved to file:***.log
这是第二个简单的打印函数,打印这句话我爱你,那么爱你!
多个装饰器同时使用
多个装饰器是可以同时使用的,至于多个装饰器使用时候内层函数的调用顺序(实际上是一个装饰器调用另一个装饰器,嵌套),可以自己加个print测试一下,仅给出示例:
#定义一个装饰器函数(功能为记录日志)
def Decorator_Logging(func):
#*args,**kwargs 用于匹配被装饰函数需要传参的情况
def wrapper_func(*args,**kwargs):
print('save log to some file:***.log ')
#将参数传入被装饰函数,仅在函数被调用的时候执行
result_of_func=func(*args,**kwargs)
print('log saved to file:***.log ')
#return 用于被装饰函数存在‘return’的情况
return result_of_func
#返回函数
return wrapper_func
#定义一个装饰器函数(功能为添加try、except)
def Decorator_Exception(func):
def wrapper_func(*args,**kwargs):
try:
print('已经进入try模块:')
result_of_func=func(*args,**kwargs)
print('try模块正常结束')
return result_of_func
except Exception as e:
print('程序有错误:')
print(e)
return wrapper_func
@Decorator_Exception
@Decorator_Logging
def test_func(a,b,c):
print('this is a sum func,result is :',a+b+c)
if __name__=="__main__":
test_func(1,2,3)
输出:
已经进入try模块:
save log to some file:***.log
this is a sum func,result is : 6
log saved to file:***.log
try模块正常结束
通过结果可以看出是上面的装饰器把下面的装饰器又嵌套了。
就像:第一个装饰器(第二个装饰器(原函数))