装饰器的个人理解
- 装饰器是一个闭包函数,最终将返回一个函数
- 返回函数前可以调用一些新增加的功能这样可以不改变源代码从而实现功能新增
- 在装饰器中如果要调用函数名称,需要显示被装饰的函数名称,需要在外层函数里调用wraps装饰器,这个装饰器在from functools import wraps引入后才可以使用【wraps 包裹,礼物 用…缠绕】
- 一个函数可以被多个装饰器装饰,即可以增加多个功能
- 如果被装饰的函数有多个参数时,写装饰器的时候内层函数参数部分要使用*args,**kwargs 增加可变参数,这样,不管是键值参数还是,多个参数都能顺利传递。
不用装饰器加功能需要修改代码的示例
import time
# 使用装饰器给代码增加添加日志的功能,把日志文件写入到log.txt中
def writelog(func):
try:
# 以追加模式a 打开一个文件,编码格式为utf-8
file=open('log.txt','a',encoding='utf-8')
# 写入访问函数的名称和时间
file.write(func.__name__+'\t')
file.write(time.asctime())
file.write('\n')
except Exception as e:
print(e.args)
finally:
# 不管发生什么错误都关闭文件
file.close()
# 定义两个函数
def func1():
writelog(func1)
print('我是函数一的功能')
def func2():
writelog(func2)
print('我是函数二的功能')
func1()
func2()
# 上面方法是通过改动源有代码实现的,其实可以通过装饰器不改变代码实现
简单的无参数装饰器
import time
# 写日志功能函数
def writelog(func):
try:
file=open('log.txt','a',encoding='utf-8')
file.write(func.__name__+'\t')
file.write(time.strftime('%Y-%m-%d %H:%M:%S'))
file.write('\n')
except Exception as e:
print(e.args)
finally:
file.close()
# 写一个闭包函数,其实就是装饰器,外部函数outfunc就是装饰器名称
def outfunc(func):
def infunc():
writelog(func)
func()
return infunc
@outfunc
def func1():
print('函数一功能')
@outfunc # 直接使用装饰器调用闭包
def func2():
print('函数功能二')
# 传统调用闭包函数,下面等价于@outfunc
# func1=outfunc(func1)
# func2=outfunc(func2)
# 调用原函数
func1()
func2()
多个装饰器,装饰无参函数
# 第一次装饰
def out_func(func):
print('外部函数启动增加功能')
def inner_func():
return '《'+func()+'》'
return inner_func
# 第二次装饰
def out_func2(func):
print('启动第二次装饰')
def inner_func():
return'$'+func()+'$'
return inner_func
@out_func2
@out_func
def bookname():
return '海底两万里'
print(bookname())
# 上述实验发现,离函数最近的最先装饰产效果
标准装饰器写法
import time
# 通过调用wraps 装饰器写入日志时把调用的函数名和函数说明文档使用统一成被装饰的函数
from functools import wraps
# 带参数传递的装饰器
# 为代码增加功能的函数,这个函数通常在装饰器中调用
def add_func():
print('我是新增功能函数')
# 第一个装饰器开始
def out_func(func):
@wraps(func)
def inner_func(*args, **kwargs):
add_func()
return func(*args, **kwargs)
return inner_func
# 再增加一个写入日志的功能
# 写日志功能函数开始
def write_log(func):
@wraps(func)
def inner_func(*args, **kwargs):
try:
file = open('log.txt', 'a', encoding='utf-8')
# 上面加入了@wraps装饰器才把这里的函数名称按被装饰的函数名显示
file.write(func.__name__ + '\t')
file.write(time.strftime('%Y-%m-%d %H:%M:%S') + '\n')
except Exception as e:
print(e.args)
finally:
file.close()
return func(*args, **kwargs)
return inner_func
@write_log
@out_func
def test(a, b, c, d):
print('我是要装饰的函数里面有参数%s,%s,%s,%s' % (a, b, c, d))
test(2, 6, 8, 10)
# 这样函数执行时使用了两个装饰器,最终写入日志的函数名称是test