装饰器的作用就是给已经存在的对象添加额外的功能
基本格式:
def decorator(func):
def inner(*args, **kwargs):
return func(*args, **kwargs)
return inner
func就是要传入的函数
可以这样使用:
def fun():
return 'hello'
decorator(fun)
也可以这样使用:
@decorator
def fun():
return 'hello'
(注意:@在装饰器这里是作为Python语法里面的语法糖写法,用来做修饰。)
装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值 也是一个函数对象。 它经常用于有以下场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计。
def decor_a(fun):
def decor_b():
print('I am decor_b')
fun()
print('I am fun')
return decor_b
上段代码可以给fun函数执行前后添加两个打印功能。
需要注意的是装饰器给对象添加完功能之后,对象名就变成了装饰器本身的内嵌函数名,比如第一段代码的装饰器内嵌函数为inner那么func对象被decorator装饰之后返回的对象名就是inner,如何解决这种问题呢?
python的内置模块functools里面有个wraps方法可以做到
比如:
def decorator(func):
@wraps(func)
def inner(*args, **kwargs):
return func(*args, **kwargs)
return inner
在python的flask框架中的路由装饰器@app.route('/index')里面,毋庸置疑肯定是用到的,因为视图函数名不能相同,当然不能个个都是‘inner’。
带参装饰器
我们也看到装饰器wraps也是带参数的,那我们是不是也可以定义带参数的装饰器呢,我们可以使用一个函数来包裹装饰器,调入这个参数。
def logs(logfile='out.log'):
def logging_decorator(fun):
@wraps(fun)
def wrapped_function(*args, **kwargs):
log_string = fun.__name__ + " Debug"
print(log_string)
# 打开logfile,并写入内容
with open(logfile, 'a') as opened_file:
opened_file.write(log_string + '\n')
return fun(*args, **kwargs)
return wrapped_function
return logging_decorator
@logs()
def decor_a():
pass
如果想要继承功能,比如写一个装饰器,我们还想继续多添加一些功能,那么就不需要重复写之前已经有的功能,直接继承过来就行,这个时候可以写类装饰器。
class Logs(object):
def __init__(self, logfile='out.log'):
self.logfile = logfile
def __call__(self, fun):
@wraps(fun)
def wrapped_function(*args, **kwargs):
log_string = fun.__name__ + " was called"
print(log_string)
# 打开logfile并写入
with open(self.logfile, 'a') as opened_file:
# 现在将日志打到指定的文件
opened_file.write(log_string + '\n')
self.notify()
# 发送一个通知
return fun(*args, **kwargs)
return wrapped_function
def notify(self):
pass
@Logs()
def decor_a():
pass
这个实现有一个优势,在于比嵌套函数的方式更加整洁,而且包裹一个函数还是使用跟以前一样的语法
现在,我们给 Logs 创建子类,来添加 email 的功能,当然这个功能不在这里详细赘述。
class EmailLogs(Logs):
''' 一个logit的实现版本,可以在函数调用时发送email给管理员 '''
def __init__(self, email='admin@myproject.com', *args, **kwargs):
self.email = email
super(EmailLogs, self).__init__(*args, **kwargs)
def notify(self):
# 发送一封email到self.email # 这里就不做实现了
pass
这里我们继续继承并重写notify方法,完成发送邮件的功能。
此时@EmailLogs 将会和 @Logs 产生同样的效果,但是在打日志的基础上,还会多发送一封邮件给管理员。