标签:装饰器、闭包。
首先我们介绍装饰器的作用:装饰器可以在代码运行期间,动态地增加功能。
我们介绍装饰器的思路:
1、函数没有返回值,没有参数,装饰器怎么写
2、函数有返回值,没有参数,装饰器怎么写
3、 函数有返回值,有参数,装饰器怎么写
先从一个简单的例子说起,例如我们要打印 到
的素数。
Python 代码:
def __is_prime(num):
if num < 2:
return False
elif num == 2:
return True
else:
for i in range(2, num):
if num % i == 0:
return False
return True
def prime_nums():
for i in range(2, 100):
if __is_prime(i):
print(i)
if __name__ == '__main__':
prime_nums()
如果我想测试这段代码运行的时间,下面是一种比较容易想到的写法,但是侵入性太强了。
Python 代码:
def prime_nums():
import time
begin = time.time()
for i in range(2, 100):
if __is_prime(i):
print(i)
end = time.time()
print('耗时', (end - begin))
即我想给业务逻辑增加一些辅助的、通用的代码,并且是低耦合、不修改源代码的,应该怎么做呢?使用装饰器。什么是装饰器呢?装饰器其实就是一个闭包函数。
我们下面的任务就是把“计时任务”和“打印素数”分离开来。于是,我们编写一个闭包函数。
说明:闭包是一个函数,是一个把函数作为返回值的函数。
Python 代码:
def display_time(func):
def wrapper():
import time
begin = time.time()
# 调用这个被传进来的函数
func()
end = time.time()
print('耗时', (end - begin))
return wrapper
上面的闭包函数,接收一个函数引用,并且返回了一个函数引用。
下面写上 @display_time
,这其实就是语法糖,表示运行的就是这个装饰器,把下面这个函数都传到装饰器代表的闭包中了,完整代码如下。
Python 代码:
def display_time(func):
def wrapper():
import time
begin = time.time()
# 调用这个被传进来的函数
func()
end = time.time()
print('耗时', (end - begin))
return wrapper
def __is_prime(num):
if num < 2:
return False
elif num == 2:
return True
else:
for i in range(2, num):
if num % i == 0:
return False
return True
@display_time
def prime_nums():
for i in range(2, 100):
if __is_prime(i):
print(i)
如果需要添加逻辑的函数有返回值,即我们的任务是计算 到
的素数有多少个。
我们这里,我们想添加一个规定: 是素数。因此,我们就要修改返回值了。我们可以在闭包函数中,增加这个逻辑,而不修改原始函数。
Python 代码:
def display_time(func):
def wrapper():
import time
begin = time.time()
# 调用这个被传进来的函数
res = func()
end = time.time()
print('耗时', (end - begin))
return res + 1
return wrapper
def __is_prime(num):
if num < 2:
return False
elif num == 2:
return True
else:
for i in range(2, num):
if num % i == 0:
return False
return True
@display_time
def prime_nums():
res = 0
for i in range(2, 100):
if __is_prime(i):
res += 1
return res
if __name__ == '__main__':
res = prime_nums()
print(res)
总结一下:

那如果函数有参数,装饰器有应该怎么写呢?
Python 代码:
def display_time(func):
def wrapper(*args):
import time
begin = time.time()
# 可以对参数作修改
print(type(*args))
new_param = args[0]
new_param *= 100
# 调用这个被传进来的函数
res = func(new_param)
end = time.time()
print('耗时', (end - begin))
return res + 1
return wrapper
def __is_prime(num):
if num < 2:
return False
elif num == 2:
return True
else:
for i in range(2, num):
if num % i == 0:
return False
return True
@display_time
def prime_nums(max_num):
res = 0
for i in range(2, max_num):
if __is_prime(i):
res += 1
return res
if __name__ == '__main__':
res = prime_nums(100)
print(res)
小结一下:

因此,使用装饰器,你可以修改参数,你也可以修改结果。
下面,如果你想使用相同的装饰器,针对不同的逻辑,有不同的调用方式,你可以给“装饰器”传递一个参数,因此,你的装饰器就要再加上一层了。
Python 代码:
def new_tips(argv):
def tips(func):
def nei(a, b):
print('start {} {}'.format(argv, func.__name__))
func(a, b)
print('stop')
return nei
return tips
@new_tips('add123')
def add(a, b):
print(a + b)
@new_tips('sub123')
def sub(a, b):
print(a - b)
add(4, 5)
sub(8, 5)
运行结果:

可以看到,装饰器 @new_tips('add123')
的参数传递到闭包中去了,可以 针对不同的参数执行不同的逻辑。
小结:

参考资料
1、“正月点灯笼”的视频讲解:https://www.bilibili.com/video/av25698102?t=99
2、2道极好的Python算法题|带你透彻理解装饰器的妙用
https://zhuanlan.zhihu.com/p/26151166
3、廖雪峰老师的 Python 教程
网友评论