基本格式:
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,如何解决这种问题呢?
比如:
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 产生同样的效果,但是在打日志的基础上,还会多发送一封邮件给管理员。