有函数如下,如何在不破坏函数的情况下为函数增加输出日志功能?
def say_nihao():
print('nihao')
很简单,我们只需要对函数进行封装即可
def log()
print('我是日志')
say_nihao()
现在我们只需要调用log()
函数即可,相信很多人都是这样写的,但是这样写有很大的弊端。
- 我们需要找到程序中所有调用
say_nihao()
的地方改成log()
- 如果现在又有一个函数
say_hello()
也需要增加输出日志功能,我们岂不是要再来一遍?也就是说我们刚才的操作是不能复用的
下面我们来介绍python中的装饰器。
对于第一个问题,我们这样进行修改:
def log()
print('我是日志')
return say_hello
say_nihao=log()
要解决第二个问题,我们只要把函数当作参数传递进去就可以了
def log(func)
print('我是日志')
return func
say_nihao=log(say_nihao)
这样我们就不需要修改程序,并且代码可以复用,而log()
我们称作装饰器
。
我们通常将装饰器中增加的功能使用wrapper()
包装起来,此时我们执行say_nihao()
就相当于执行wrapper()
def log(func)
def wrapper():
print('我是日志')
return func
return wrapper
say_nihao=log(say_nihao)
在面向切面编程(AOP)中,我们将log()
称为切面。将切面和具体函数结合的地方,也就是say_nihao=log(say_nihao)
称为切入点。
如果say_nihao()
函数有参数 ,我们可以这样:
def log(func)
def wrapper(*args, **kwargs):
print('我是日志')
return func(*args, **kwargs)
return wrapper
say_nihao=log(say_nihao)
*args
和**kwargs
作用不再说明。
因为实际上调用say_nihao()相当于调用wrapper(),所以需要按照wrapper()的参数表进行传递,然后在wrapper()的函数体中传给say_nihao()
在python中为say_nihao=log(say_nihao)
提供了一种语法糖:@
def log(func)
def wrapper(*args, **kwargs):
print('我是日志')
return func(*args, **kwargs)
return wrapper
@log
def say_nihao():
print('nihao')
这里说一下参数的问题,如果@log
没有参数,那么传递到装饰器log()
函数里的是say_nihao对象,如果@log(a)
有参数,那么那么传递到装饰器log()
函数里的是@log(a)
里面的参数a,而say_nihao对象将被传递到wrapper()函数里。
基于此,如果装饰器本身需要参数,那么可以这么些:
def log(value):
def decorator(func):
def wrapper(*args, **kwargs)
print(value)
return func(*args, **kwargs)
return wrapper
return decorator
@log('我是日志')
def say_nihao():
print('nihao')
调用被修饰函数,相当于调用修饰函数的返回值
如果@log
没有参数,相当于使用log函数进行修饰,没有参数时不要携带括号
如果@log( )
有参数,相当于使用log函数的返回值进行修饰