Python装饰器
装饰器是个啥
使用闭包可以在不修改源码的前提下,为现有函数添加新的功能。
使用装饰器和闭包结合,可以使访问方式更加简单,更加实用。
装饰器和闭包息息相关。
【实例】使用装饰器和闭包结合给函数增加日志功能
# 闭包的应用:实现日志功能
import time
def write_log(func):
try:
file = open('log.txt', 'a', encoding='utf-8')
# 写入相关信息(访问的函数名称,访问的时间)
file.write(func.__name__)
file.write('\t')
# 写入访问时间
file.write(time.asctime())
file.write('\n')
except Exception as e:
print(e.args)
finally:
# 关闭文件
file.close()
# 定义闭包
def func_out(func):
def func_in():
write_log(func)
func()
return func_in
# 使用了装饰器,效果和在调用函数前func1 = func_out(func1)一样
@func_out
def func1():
print('我是功能1')
@func_out
def func2():
print('我是功能2')
if __name__ == "__main__":
func1()
func2()
运行结果:
func1 Tue Nov 1 10:49:17 2022
func2 Tue Nov 1 10:49:17 2022
很显然,装饰器可以更简化地使用闭包。只需要在原函数前面添加‘@闭包外函数名’就可以达到在执行函数之前写一句类似func1 = func_out(func1)调用闭包的语句,非常的方便。
【注意】装饰器的名称应该是闭包外部函数的名称
多装饰器的使用
【实例】不修改一个函数源代码的前提下,给返回的“西游记”增加书名号
# 定义闭包1
def func_out(func):
print('闭包1开始装饰……')
def func_in():
return '《' + func() + '》'
# 给‘西游记’加书名号
return func_in
# 定义闭包2
def func_out2(func):
print('闭包2开始装饰……')
def func_in():
return '$' + func() + '$'
# 给‘西游记’加‘$’符号
return func_in
@func_out2
# 相当于在执行函数之前先执行book_name = func_out2(book_name)
@func_out
# 同理book_name = func_out(book_name)
def book_name():
return '西游记'
print(book_name())
运行结果:
闭包1开始装饰……
闭包2开始装饰……
《西游记》 《西游记》 《西游记》
在使用多装饰器的时候,最靠近被装饰函数的闭包先执行,远离被装饰函数的闭包后执行。
指定参数个数的装饰器
之前的装饰器都应用在原函数不涉及传参的情况,那么当原函数涉及传参的时候,应该如何设计装饰器呢?
【实例】指定参数个数的装饰器
# 定义新增功能
def add_func():
print('我是新增的功能')
# 定义闭包
def func_out(func):
def func_in(x, y):
add_func()
func(x, y)
return func_in
@ func_out # 原函数使用装饰器
def test(a, b):
print('a = {0} b = {1}'.format(a, b))
test(1, 2)
运行结果:
我是新增的功能
a = 1 b = 2
这里在定义闭包的时候,如果没有其他需求,可以将闭包的内函数func_in()的形参和其函数体里面用于运行原函数的对象func()的形参设置成对应原函数的形参个数,然后平行地传进实参即可,而对于其他形参数量不同的元函数,可以多写几个闭包,然后用装饰器执行。
思考:但是,似乎这种方式有点麻烦,如果所有函数都需要同样的新增功能,那岂不是要给所有形参数量不同的函数挨个写闭包?!有没有可以通用所有函数的装饰器呢?
通用装饰器
只需要在设置闭包的时候,把前面传参数的部分的形参由固定参数个数修改为可变参数:*args, **kwargs 即可。
【实例】通用装饰器适应所有参数数量的函数
# 定义新增功能
def add_func():
print('我是新增的功能')
# 定义闭包
def func_out(func):
def func_in(*args, **kwargs): # 使用可变参数
add_func()
func(*args, **kwargs) # 这里也需要可变参数
return func_in
@func_out # 装饰不需要传参的函数
def test():
print('原函数1')
@func_out # 装饰需要传递两个参数的函数
def test1(a, b):
print('a = {0} b = {1}'.format(a, b))
@func_out # 装饰需要传递三个参数的函数
def test2(a, b, c):
print('a = {0} b = {1} c = {2}'.format(a, b, c))
test()
test1(1, 2)
test2(1, 2, 3)
运行结果:
我是新增的功能
原函数1
我是新增的功能
a = 1 b = 2
我是新增的功能
a = 1 b = 2 c = 3