装饰器原理
统计1—100奇数,并计算用时。
import time
def print_odds():
start_time = time.time()
for i in range(100):
if 1 % 2 == 1:
print(i)
end_time = time.time()
print("it takes {} s to find all the olds".format(end_time-start_time))
if __name__ == '__main__':
print_odds()
观察发现,以上代码主要由函数逻辑(查找奇数)和辅助功能(记录时间),但是两个功能耦合在一起了。
- 缺点:使修改起来不方便,容易引起bug
- 能不能将复制函数从主要功能函数中抽取出来?
import time
def print_odds():
for i in range(100):
if 1 % 2 == 1:
print(i)
def count_time(func):
start_time = time.time()
func()
end_time = time.time()
print("it takes {} s to find all the olds".format(end_time-start_time))
if __name__ == '__main__':
count_time(print_odds)
将辅助函数(记录时间)抽离成函数count_time,在辅助函数count_time中调用主要功能函数print_odds,
- 优点:解耦没函数功能分离
- 缺点:要通过辅助函数来调用主要函数功能,不方便。
- 目标:能不能在调用主函数时自动完成对时间的统计?
import time
def print_odds():
for i in range(100):
if 1 % 2 == 1:
print(i)
# 闭包本质上是一个函数
# 闭包函数的传入参数和返回值都是函数
# 闭包函数的返回值函数是对传入函数进行增强后的结果
def count_time_wrapper(func):
def improved_func():
start_time = time.time()
func()
end_time = time.time()
print("it takes {} s to find all the olds".format(end_time - start_time))
return improved_func
if __name__ == '__main__':
print_odds = count_time_wrapper(print_odds)
print_odds()
以上代码可以通过辅助函数来调用主要函数功能,但是调用时还是比较麻烦。
import time
def count_time_wrapper(func):
def improved_func():
start_time = time.time()
func()
end_time = time.time()
print("it takes {} s to find all the olds".format(end_time - start_time))
return improved_func
@count_time_wrapper
def print_odds():
for i in range(100):
if 1 % 2 == 1:
print(i)
if __name__ == '__main__':
print_odds()
通过装饰器进行函数增强,只是一种语法糖,本质上遇上一个完全一致。
闭包传参,返回值
问题:对于含有返回值的函数,调用闭包增强函数后,不能成功返回,但成功的增强了辅助函数。
对于含有参数的函数,调用闭包增强后,不能成功接收参数。
案例如下:
import time
def count_time_wrapper(func):
def improved_func():
start_time = time.time()
func()
end_time = time.time()
print("it takes {} s to find all the olds".format(end_time - start_time))
return improved_func
def count_odds(lim=100):
cnt = 0
for i in range(lim):
if i % 2 == 1:
cnt += 1
return cnt
if __name__ == '__main__':
print('增强前,不传参')
count_odds()
print('--------------')
print('增强后,手动调用闭包,不传参')
count_odds = count_time_wrapper(count_odds)
print(count_odds())
print('---------------------------')
print('增强前,传参')
count_odds(lim=90)
print('--------------')
print('增强后,手动调用闭包,传参')
count_odds = count_time_wrapper(count_odds)
print(count_odds(lim=90))
运行结果
增强前,不传参
it takes 1.0967254638671875e-05 s to find all the olds
--------------
增强后,手动调用闭包,不传参
it takes 8.106231689453125e-06 s to find all the olds
it takes 1.5974044799804688e-05 s to find all the olds
None
---------------------------
增强前,传参
Traceback (most recent call last):
File "/Users/lidong/Desktop/zhouyu/project/crawl-lidong/crawlab_env/lib/python3.9/site-packages/IPython/core/interactiveshell.py", line 3441, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-4-68245ad7e616>", line 9, in <module>
count_odds(lim=90)
TypeError: improved_func() got an unexpected keyword argument 'lim'
解决方案: 引入变量,将原函数返回结果返回给增强函数,解决增强函数返回值为None问题。
添加参数,将参数传给原函数,使原函数可以接收到参数。
案例如下(手动调用闭包):
import time
def count_time_wrapper(func):
def improved_func(*args, **kwargs):# 添加参数,将参数传给原函数,使原函数可以接收到参数
start_time = time.time()
ret = func(*args, **kwargs) #引入变量,将原函数返回结果返回给增强函数,解决增强函数返回值为None问题
end_time = time.time()
print("it takes {} s to find all the olds".format(end_time - start_time))
return ret
return improved_func
def count_odds(lim=100):
cnt = 0
for i in range(lim):
if i % 2 == 1:
cnt += 1
return cnt
if __name__ == '__main__':
print('增强前,不传参')
count_odds()
print('--------------')
print('增强后,手动调用闭包,不传参')
count_odds = count_time_wrapper(count_odds)
print(count_odds())
print('---------------------------')
print('增强前,传参')
count_odds(lim=90)
print('--------------')
print('增强后,手动调用闭包,传参')
count_odds = count_time_wrapper(count_odds)
print(count_odds(lim=90))
运行结果:
增强前,不传参
--------------
增强后,手动调用闭包,不传参
it takes 5.9604644775390625e-06 s to find all the olds
50
---------------------------
增强前,传参
it takes 4.76837158203125e-06 s to find all the olds
--------------
增强后,手动调用闭包,传参
it takes 3.814697265625e-06 s to find all the olds
it takes 6.9141387939453125e-06 s to find all the olds
案例如下(自动调用闭包):
import time
def count_time_wrapper(func):
def improved_func(*args, **kwargs):# 添加参数,将参数传给原函数,使原函数可以接收到参数
start_time = time.time()
ret = func(*args, **kwargs) #引入变量,将原函数返回结果返回给增强函数,解决增强函数返回值为None问题
end_time = time.time()
print("it takes {} s to find all the olds".format(end_time - start_time))
return ret
return improved_func
@count_time_wrapper
def count_odds(lim=100):
cnt = 0
for i in range(lim):
if i % 2 == 1:
cnt += 1
return cnt
if __name__ == '__main__':
print('增强前,不传参')
count_odds()
print('--------------')
print('增强后,自动调用闭包,不传参')
print(count_odds())
print('---------------------------')
print('增强前,传参')
count_odds(lim=90)
print('--------------')
print('增强后,自动调用闭包,传参')
print(count_odds(lim=90))
运行结果
增强前,不传参
it takes 1.3113021850585938e-05 s to find all the olds
--------------
增强后,自动调用闭包,不传参
it takes 1.0013580322265625e-05 s to find all the olds
50
---------------------------
增强前,传参
it takes 7.867813110351562e-06 s to find all the olds
--------------
增强后,自动调用闭包,传参
it takes 7.152557373046875e-06 s to find all the olds
45
装饰器面试题
- 装饰器在第一次调用被装饰函数时进行增强。
- 当有多个装饰器时,怎么执行。
- 手动执行
def wrapper1(func1):
print('set fun1')
def improved_func1():
print('call func1')
func1()
return improved_func1
def wrapper2(func2):
print('set fun2')
def improved_func2():
print('call func2')
func2()
return improved_func2
# @wrapper1
# @wrapper2
def original_func():
print('主函数')
if __name__ == '__main__':
print(original_func.__name__) # original_func = original_func
original_func = wrapper2(original_func)
print(original_func.__name__) # original_func = improved_func2
original_func = wrapper1(original_func)
print(original_func.__name__) # original_func = improved_func1
original_func()
运行结果
original_func
set fun2
improved_func2
set fun1
improved_func1
call func1
call func2
主函数
- 装饰器
def wrapper1(func1):
print('set fun1')
def improved_func1():
print('call func1')
func1()
return improved_func1
def wrapper2(func2):
print('set fun2')
def improved_func2():
print('call func2')
func2()
return improved_func2
@wrapper1
@wrapper2
def original_func():
print('主函数')
if __name__ == '__main__':
original_func()
运行结果
set fun2
set fun1
call func1
call func2
主函数