装饰器是AOP编程思想,给主体函数增加功能,又不让代码入侵到主体函数中,实现高内聚,低耦合。
如果装饰功能部分代码也需要参数的话,可以在原来的两层函数外面再加一层,专门用来接收参数。
举例:给一个函数增加日志,这里的日志就简化为一个log.txt里面写入一句话。
写法1:装饰器函数
def decoLog(filename):
def log(func):
def wrapper(*args, **kwargs):
log = '函数{}日志'.format(func.__name__)
with open(filename, 'a', encoding='utf-8') as f:
f.write(log + '\n')
func(*args, **kwargs)
return wrapper
return log
@decoLog('log1.txt')
def tony(num):
print(tony.__name__+str(num))
tony(1)
执行结果:
控制台:
wrapper1
log1.txt:
函数tony日志
写法2:类装饰器
__call__方法
class DecoLog:
def __init__(self,filename):
self.filename = filename
def __call__(self, func):
def wrapper(*args, **kwargs):
log = '函数{}日志'.format(func.__name__)
with open(self.filename, 'a', encoding='utf-8') as f:
f.write(log + '\n')
func(*args, **kwargs)
return wrapper
@DecoLog('log2.txt')
def tony(num):
print(tony.__name__+str(num))
tony(2)
执行结果:
控制台:
wrapper2
log2.txt:
函数tony日志
写法3:类装饰器
生成类的对象,对象调用方法
class DecoLog():
def __init__(self,filename):
self.filename = filename
def log(self,func):
def wrapper(*args, **kwargs):
log = f'函数{func.__name__}日志'
with open(self.filename, 'a', encoding='utf8') as f:
f.write(log +'\n')
func(*args, **kwargs)
return wrapper
decoLog = DecoLog('log3.txt')
@decoLog.log
def tony(num):
print(tony.__name__+str(num))
tony(3)
执行结果:
控制台:
wrapper3
log3.txt:
函数tony日志
总结:以上就是3中装饰器的实现,都是使tony函数变成了wrapper函数。@写法只是语法糖。
发现一个问题,日志文件中打印的是tony,而控制台是wrapper。这是因为执行tony函数时tony函数已经变成wrapper函数,tony.__name__获取的名字是wrapper函数名。python中为了解决这个问题,内置了wraps,装饰在最终取代原函数的函数上面。
from functools import wraps
def decoLog(filename):
def log(func):
@wraps(func) //加在最终返回的取代tony的函数上
def wrapper(*args, **kwargs):
log = '函数{}日志'.format(func.__name__)
with open(filename, 'a', encoding='utf-8') as f:
f.write(log + '\n')
func(*args, **kwargs)
return wrapper
return log
@decoLog('log1.txt')
def tony(num):
print(tony.__name__+str(num))
tony(1)
控制台打印结果:
tony1