为什么要用装饰器:
先看一个例子:
下面是一个寻找2-num之间的质数, 并打印其耗时的代码
def cout_prime_number(num):
t1 = time.time()
prime_number = []
for i in range(2, num):
cout = 0
for j in range(2, i): # 当 i= 7, 被2, 3, 4, 5, 6都不能整除, 所以cout不会自加
if i % j == 0:
cout += 1
if cout == 0:
prime_number.append(i)
t2 = time.time()
print('耗时:', t2-t1)
return prime_number
a = cout_prime_number(100)
print(a)
输出:
耗时: 0.0005745887756347656
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
若我们又有一个函数,是寻找2-nums的偶数, 并打印其耗时, 我们耗时代码又要在首尾写一遍,可读性较差,相当于写了重复代码
t1 = time.time()
t2 = time.time()
prnt('耗时:', t2-t1)
这个时候我们用装饰器,来把那些重复的代码装饰起来,方便调用,如下:
import time
def display(func):
def wrapper(num):
t1 = time.time()
ret = func(num)
t2 = time.time()
print('耗时:', t2-t1)
return ret
return wrapper
@display
def cout_prime_number(num):
prime_number = []
for i in range(2, num):
cout = 0
for j in range(2, i): # 当 i= 7, 被2, 3, 4, 5, 6都不能整除, 所以cout不会自加
if i % j == 0:
cout += 1
if cout == 0:
prime_number.append(i)
return prime_number
a = cout_prime_number(100)
print(a)
输出:
耗时: 0.0005745887756347656
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
其中我们新增加了一个装饰器display,他是一个函数,而且返回的也是一个函数,可以看出,我们cout_prime_number这个函数中的耗时计算代码已经没有了,被我们写到了装饰器display里,装饰器display接收的参数func就是cout_prime_number这个函数,返回的是wrapper这个函数,当然wrapper也可以取其他的名字,而wrapper函数接收的参数就是func函数的参数num,
外面这样写wrapper(num), 里面也要写func(num), 这里func就是cout_prime_number, 有时候不确定cout_prime_number参数的个数,可以用*args和**kwargs代替,wrapper里面的参数要和func里面保持一致,即wrapper(*args,**kwargs), 里面也要写func(*args,**kwargs), 并且要把func的函数值ret返回, 而display要把wrapper返回, 反正格式就这样写,例如:
import time
def display(func):
def wrapper(*args, **kwargs):
t1 = time.time()
ret = func(*args, **kwargs)
t2 = time.time()
print('耗时:', t2-t1)
return ret
return wrapper
@display
def cout_prime_number(num):
prime_number = []
for i in range(2, num):
cout = 0
for j in range(2, i): # 当 i= 7, 被2, 3, 4, 5, 6都不能整除, 所以cout不会自加
if i % j == 0:
cout += 1
if cout == 0:
prime_number.append(i)
return prime_number
a = cout_prime_number(100)
print(a)
输出:
耗时: 0.0005745887756347656
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
在cout_prime_number函数前面加@display就是装饰器的用法,这其实等价于如下插入的代码段:
就是这样两句:
cout_prime_number = display(cout_prime_number )
a = cout_prime_number(100)
装饰器就是把函数cout_prime_number 作为参数传进display这个函数中
import time
def display(func):
def wrapper(*args, **kwargs):
t1 = time.time()
ret = func(*args, **kwargs)
t2 = time.time()
print('耗时:', t2-t1)
return ret
return wrapper
def cout_prime_number(num):
prime_number = []
for i in range(2, num):
cout = 0
for j in range(2, i): # 当 i= 7, 被2, 3, 4, 5, 6都不能整除, 所以cout不会自加
if i % j == 0:
cout += 1
if cout == 0:
prime_number.append(i)
return prime_number
cout_prime_number = display(cout_prime_number )
a = cout_prime_number(100)
print(a)
输出:
耗时: 0.0005745887756347656
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
这种,要对装饰器再包装一次,display变成了三层,照着这样写就行
比如,我们想在在打印时间的同时,还打印文本text,例如:
import time
def display(text):
def decorate(func):
def wrapper(*args, **kwargs):
t1 = time.time()
ret = func(*args, **kwargs)
t2 = time.time()
print('text:', text)
print('耗时:', t2-t1)
return ret
return wrapper
return decorate
@display('2022年12月9日晚测试')
def cout_prime_number(num):
prime_number = []
for i in range(2, num):
cout = 0
for j in range(2, i): # 当 i= 7, 被2, 3, 4, 5, 6都不能整除, 所以cout不会自加
if i % j == 0:
cout += 1
if cout == 0:
prime_number.append(i)
return prime_number
a = cout_prime_number(100)
print(a)
输出:
text: 2022年12月9日晚测试
耗时: 0.0009658336639404297
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
上面带输入参数的装饰器用法相当于:
cout_prime_number = display('2022年12月9日晚测试')(cout_prime_number)
a = cout_prime_number(100)
print(a)
一些注意的地方,我看一般都会在warpper前面加上一句:
@wraps(func)
wraps是python包functools自带的
说是不加,有些依赖函数签名的代码执行就会出错。那也就是说一般情况可以不加?最好还是加一下。
原因:装饰器 - 廖雪峰的官方网站
import time
from functools import wraps
def display(text):
def decorate(func):
@wraps(func)
def wrapper(*args, **kwargs):
t1 = time.time()
ret = func(*args, **kwargs)
t2 = time.time()
print('text:', text)
print('耗时:', t2-t1)
return ret
return wrapper
return decorate
def display3(func):
@wraps(func)
def wrapper(*args, **kwargs):
t1 = time.time()
ret = func(*args, **kwargs)
t2 = time.time()
print('耗时:', t2-t1)
return ret
return wrapper