装饰器
由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。装饰器是将一个函数镶嵌在另一个函数中进行重复使用的目的,增加函数的使用方式,但是不用写过多冗余的代码。
装饰器本质上是一个函数,他可以让其他函数在不需要做任何代码修改的前提下增加额外的功能,装饰器的返回值也是一个函数对象,它经常用于有切面需求的场景,比如:插入日志,性能测试,权限校验等,有了装饰器,我们就可以抽离出大量与函数本身无关 的雷同代码并继续重用。装饰器的作用就是为已经存在的对象添加额外的功能。
先看一个简单例子:
def func():
print('%s is running' %func.__name__)
现在想增加新的功能,打开一个文件并写入内容,
def func():
ss = func.__name__ + 'was called'
with open('logfile.txt', 'w') as logfile:
logfile.write(ss)
print('%s is running' %func.__name__)
如果有不同的函数func1、func2都需要增加同样的功能,我们怎么避免重复写代码呢?我们可以将功能函数单独定义。
def write_logfile(func):
ss = func.__name__
with open(ss+'_logfile.txt','w') as log_file:
log_file.write(ss+' was called')
func()
def f():
print(f.__name__ + ' is running')
write_logfile(f)
虽然功能完全一样,但是逻辑却变了,从执行f()调用write_logfile变成了write_logfile(f),如果我们仍然想通过f()来执行呢?可以这样做
#装饰器
def write_logfile(func1):
def wrapfunction():
ss = func1.__name__
with open(ss+'_logfile.txt', 'w') as log_file:
log_file.write(ss+ ' was called')
return func1()
return wrapfunction
def func1():
print(' %s is running' %func1.__name__)
func1 = write_logfile(func1)
这就是一个简易的装饰器,它们用来封装函数,@符号是装饰器的语法糖,通过@,我们不在需要用write_logfile(func1),只需要在定义func1函数时在前面一行加上@write_logfile即可。
但此时会发现一个问题,原函数的元信息不见了,例如func1.__name__不再是func1,而是wrapfunction,因为此时func1等价于write_logfile(func1),函数的名字和注释文档被重写了,所以它的__name__等变成了wrapfunction函数的信息了。python的functools.wraps可以解决这个问题,它把原函数的元信息拷贝到装饰器函数中,
from functools import wraps
def write_logfile(func1):
@wraps(func1)
def wrapfunction():
ss = func1.__name__
with open(ss+'_logfile.txt', 'w') as log_file:
log_file.write(ss+ ' was called')
return func1()
return wrapfunction
@write_logfile
def func1():
print(' %s is running' %func1.__name__)
带参数的装饰器
from functools import wraps
def write_logfile(logfile):
def decorator(func):
@wraps(func)
def wrapfunction(*args):
with open(logfile, 'w') as log_file:
log_file.write(ss+ ' was called')
return func(*args)
return wrapfunction
return decorator
@write_logfile(logfile='func1_logfile.txt')
def func(x=1):
print(x*2)
注:这是否可以理解为是一个带有参数的闭包
闭包:
def outer():
x = 1
def inner()
print x
return inner
f = outer()
从变量的生命周期来看,x对函数outer来说是局部变量,只有当outer运行时它才存在。只有当outer返回是才能调用inner,所以在调用inner时x就应该不存在了,但返回的inner却能正常运行。事实上python支持一种名为函数闭包的特性,在非全局作用域定义的inner函数定义是记得外层命名空间是怎么样的,inner函数包含了外层作用域变量,通过查看它的func_closure属性可以看出这种函数闭包特性
类装饰器
class logit(object):
def __init__(self, logfile='log.txt'):
self.logfile = logfile
def __call(self, func):
@wraps(func)
def wrapfunction(*arg):
ss = func.__name__ + ' was called'
with open(logfile, 'w') as file:
file.write(ss)
return func(*arg)
return wrapfunction
@logit
def func(*args):
...