知识内容:
1.装饰器介绍
2.开放封闭原则
3.装饰器逐步实现
4.参数与装饰器
5.多个装饰器装饰一个函数
一、装饰器介绍
1.装饰器的定义
定义: 定义一个函数,在运行时动态增加功能,又不改动函数本身的代码和调用方式
2.装饰器的功能及本质
功能: 在不修改原函数及其调用方式的情况下对原函数功能进行拓展
本质: 就是一个函数,也可以说是一个闭包函数
3.装饰器的原则
(1)不能修改被装饰的函数的源代码
(2)不能修改被装饰的函数的调用方式
以上两点说白了就算装饰器对被装饰的函数来说是透明的,也就是被装饰的函数中根本不知道装饰函数的存在,
被装饰的函数还是和原来一样
4.一个简单的装饰器
1 # 统计程序运行时间的装饰器: 2 import time 3 4 def timmer(func): 5 def warpper(*args, **kwargs): 6 start_time = time.time() 7 func() 8 stop_time = time.time() 9 print("The func run time is %s" % (stop_time-start_time))10 return warpper11 12 @timmer13 def test1():14 time.sleep(3)15 print("In the test1")16 17 18 test1()
上面这个函数可能你第一眼看的很懵逼,我也是一样,不用急,慢慢把后面看完就能理解上面的代码了
5.实现装饰器需要的知识
实现装饰器的知识储备:
(1)函数即"变量"
(2)高阶函数
(3)嵌套函数
高阶函数 + 嵌套函数 -> 装饰器
关于以上内容可以见我另外一篇博客,具体位置:
二、开放封闭原则
开放封闭原则是开发中的一个重要概念,主要内容如下:
- 对扩展是开放的
- 对修改是封闭的
当然在python的开发中使用装饰器可以很好地实现这个原则,因为装饰器可以在不修改原函数及其调用方式的情况下对原函数功能进行拓展
三、装饰器逐步实现
1.不修改函数源代码
在不修改函数内部代码的情况下实现增加功能可以借助高阶函数来实现,具体实例如下
1 import time 2 3 def decorator(func): 4 start_time = time.time() 5 func() 6 end_time = time.time() 7 print("The run time is", end_time - start_time) 8 9 def test1():10 time.sleep(3)11 print("in the test1")12 13 def test2():14 time.sleep(3)15 print("in the test2")16 17 decorator(test1)18 decorator(test2)
以上的代码中通过不修改函数内源代码的方式实现增加统计时间功能,但是改变了函数的调用方式,在本质上说还不是真正的装饰器
2.不修改函数的调用方式
在不修改函数调用方式的情况下实现增加功能可以借助嵌套函数来实现,具体实例如下
1 import time 2 def timer(func): # timer(test1) func = test1 3 def decorator(): 4 start_time = time.time() 5 func() 6 end_time = time.time() 7 print("The run time is", end_time - start_time) 8 return decorator 9 10 def test1():11 time.sleep(3)12 print("in the test1")13 14 def test2():15 time.sleep(3)16 print("in the test2")17 18 test1 = timer(test1)19 test1() # 实际上是在执行decorator20 test2 = timer(test2)21 test2() # 实际上是在执行decorator
以上的代码中其实不光用到了嵌套函数,也用到了高阶函数,可以说实现了在不修改原函数及其调用方式的情况下对原函数功能进行拓展,但是这还不是真正的装饰器
3.真正的装饰器
(1)装饰器实现
1 import time 2 def timer(func): # timer(test1) func = test1 3 def decorator(): 4 start_time = time.time() 5 func() 6 end_time = time.time() 7 print("The run time is", end_time - start_time) 8 return decorator 9 10 @timer # test1 = timer(test1)11 def test1():12 time.sleep(3)13 print("in the test1")14 15 @timer # test2 = timer(test2)16 def test2():17 time.sleep(3)18 print("in the test2")19 20 test1() # 实际上是在执行decorator21 test2() # 实际上是在执行decorator
注: @timer本质是test1 = timer(test1)
上面的实例就是一个真正的装饰器,与在2.不修改函数的调用方式中的代码相比较,只是把test1 = timer(test1)换成了@timer并加在要装饰的函数的函数名前
(2)装饰器实现详解
以(1)代码中的test1为例:
- 导入模块,定义函数timer
- 运行@timer也就是在调用timer,把test1函数作为变量传给timer函数,定义decorator函数,再将decorator函数返回给test1
- 运行test1(),此时test1在前面已经接受了返回的值(decorator函数),也就是在运行decorator函数
- 运行decorator函数,decorator函数在@timer运行时已经传入了开始的test1
- 在decorator函数中调用之前传入的test1函数对应的函数func,其前后的代码即是增加的功能
了解装饰器程序运行的流程最好的方法就是单步调试,一步一步的去看程序运行的过程
pycharm单步调试看网友的这篇博客:
4.装饰器的固定结构
1 import time 2 def wrapper(func): # 装饰器 3 def inner(*args, **kwargs): 4 '''函数执行之前的内容扩展''' 5 ret = func(*args, **kwargs) 6 '''函数执行之后的内容扩展''' 7 return ret 8 return inner 9 10 @wrapper # f=wrapper(f)11 def f():12 time.sleep(1)13 print('fdfgdg')14 f()
以上是装饰器的固定格式,并且被装饰的函数可以带参数,也可以不带参数,具体带参数的装饰器见下面详解
四、参数与装饰器
1.被修饰的函数带有参数
1 import time 2 3 4 def timer(func): # timer(test1) func = test1 5 def decorator(*args, **kwargs): 6 start_time = time.time() 7 func(*args, **kwargs) 8 end_time = time.time() 9 print("The run time is", end_time - start_time)10 return decorator11 12 13 @timer # test1 = timer(test1)14 def test1():15 time.sleep(3)16 print("in the test1")17 18 19 @timer # test2 = timer(test2)20 def test2(name, age):21 time.sleep(3)22 print("in the test2")23 print("name: %s, age: %d" % (name, age))24 25 26 test1() # 实际上是在执行decorator27 test2("wyb", 21) # 实际上是在执行decorator
2.带参数的装饰器
当加了很多装饰器时,想把某装饰器给去掉了,但是那么多的代码,一个个去很麻烦,那么我们可以用带参数的装饰器去装饰它,这就他就像一个开关一样,要的时候就调用了,不用的时候就去掉。给装饰器里面传个参数,那么那个语法糖也要带个括号。在语法糖的括号内传参。在这里,我们可以用三层嵌套,弄一个标识为去标识
1 # 带参数的装饰器:(相当于开关)为了给装饰器传参 2 F = True # 为True时就加上装饰器 3 # F = False # 为False时就去掉装饰器 4 5 6 def outer(flag): 7 def wrapper(func): 8 def inner(*args, **kwargs): 9 if flag:10 print('before')11 ret = func(*args, **kwargs)12 print('after')13 else:14 ret = func(*args, **kwargs)15 return ret16 return inner17 return wrapper18 19 20 @outer(F) # outer(F) = wrapper -> @wrapper -> f1 = wrapper(f1)21 def f1():22 print('in f1')23 24 25 @outer(F) # outer(F) = wrapper -> @wrapper -> f2 = wrapper(f2)26 def f2():27 print('in f2')28 29 30 f1()31 f2()
五、多个装饰器装饰一个函数
1 def before(func): #定义装饰器 2 def wrapper(*args, **kwargs): 3 print('Before function called.') 4 return func(*args, **kwargs) 5 return wrapper 6 7 def after(func): #定义修装饰器 8 def wrapper(*args, **kwargs): 9 result = func(*args, **kwargs)10 print('After function called.')11 return result12 return wrapper13 14 @before15 @after16 def test(): #同时使用两个装饰器装饰函数17 print(3)18 test() #调用被装饰的函数
运行结果:
1 Before function called.2 33 After function called.
1 def s(fun): 2 def inner(*args, **kwargs): 3 print('in s: before') 4 ret = fun(*args, **kwargs) 5 print('in s: after') 6 return ret 7 return inner 8 9 10 def f(fun):11 def inner(*args, **kwargs):12 print('in f: before')13 ret = fun(*args, **kwargs)14 print('in f: after')15 return ret16 return inner17 18 19 @s20 @f21 def func():22 print('in the func')23 24 func()
运行结果:
1 in s: before2 in f: before3 in the func4 in f: after5 in s: after