十九、python沉淀之路--装饰器
一、实现装饰器的预备知识
装饰器 = 高阶函数 + 函数嵌套 + 闭包
1、高价函数定义:
1.函数接收的参数是一个函数名
2.函数的返回值是一个函数名
3.满足上述条件任意一个,都可称之为高阶函数
例1:铺垫
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 import time 2 #例1 3 def fn(): 4 print('这是被调用函数') 5 time.sleep(2) 6 7 def test(func): 8 print('高阶函数将要开始运行') 9 start_time = time.time() 10 func() 11 end_time = time.time() 12 print('被调用函数的运行时间%s'%(end_time - start_time)) 13 14 test(fn)
例2
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #例2 2 import time 3 def fn(): 4 print('这是被调用函数') 5 time.sleep(2) 6 def test(func): 7 print('正在执行高阶函数') 8 return func 9 #第一种调用 10 f = test(fn) 11 print(f) 12 f() 13 14 ## 第二种调用,升级版 :将调用函数test(func)的返回值赋值给一个与被调用函数fn()同名的变量fn,最后实现的效果就是 15 #(1)不改变被调用函数fn()的源代码 16 #(2) 不改变被调用函数fn()的调用方式 17 fn = test(fn) 18 print(fn) 19 fn()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 正在执行高阶函数 2 <function fn at 0x0000017065C999D8> 3 这是被调用函数 4 正在执行高阶函数 5 <function fn at 0x0000017065C999D8> 6 这是被调用函数
升级版 :将调用函数test(func)的返回值赋值给一个与被调用函数fn()同名的变量fn,最后实现的效果就是
(1)不改变被调用函数fn()的源代码
(2) 不改变被调用函数fn()的调用方式
例3 这种情况出现的结果是 多运行了一行
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # 例3 2 import time 3 def fn(): 4 print('这是被调用函数') 5 time.sleep(2) 6 7 def test(func): 8 print('开始执行调用函数') 9 start_time = time.time() 10 func() 11 end_time = time.time() 12 return func # 这种情况出现的结果是 多运行了一行 13 14 fn = test(fn) 15 fn()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 开始执行调用函数 2 这是被调用函数 3 这是被调用函数
例4
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #例4 2 def fn(): 3 print('这是被调用函数') 4 def test(func): 5 print('正在执行高阶函数') 6 res = func() 7 print('被调用函数执行完毕') 8 return res 9 10 fn = test(fn) 11 # fn() # 报错 :TypeError: 'NoneType' object is not callable
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 正在执行高阶函数 2 这是被调用函数 3 被调用函数执行完毕
即使赋了一个值,也还是解决不了多打印一行的结果。所以单层函数解决不了这个问题
高阶函数总结:
1、函数接收的参数是一个函数名
作用:在不修改函数源代码的前提下,为函数添加新功能。
不足:会改变函数的调用方式。
2、函数的返回值是一个函数名:
作用:不修改函数的调用方式
不足:不能添加新功能。
二、嵌套函数
定义:
1 def father(name): 2 print('%s is from father'%name) 3 def son(): 4 print('I am BeiJing,My fahter is %s'%name) 5 def grandson(): 6 print('I am HaiDian,My grandfather is %s'%name) 7 grandson() 8 son() 9 # print(locals()) 10 11 father('china')
1 china is from father 2 I am BeiJing,My fahter is china 3 I am HaiDian,My grandfather is china
详解在我的另外一篇博客里面:http://www.cnblogs.com/jianguo221/p/8984618.html
三、实现装饰器(步骤进化)(重点中的重点)
1 #进化版############################################################3 2 import time 3 def outer(func): 4 def inner(): 5 print('开始运行被测试函数') 6 start_time = time.time() 7 func() 8 end_time = time.time() 9 print('被测试函数的运行时间是:%s'%(start_time - end_time)) 10 return inner 11 12 def test(): 13 print('正在运行这个测试函数') 14 time.sleep(2) 15 # 慢慢实现装饰器 16 #第一种调用方式 17 f = outer(test) #这条语句返回的就是inner的地址,即函数体 18 f() 19 20 #第二种调用方式 21 inner = outer(test) ##这条语句返回的就是inner的地址,即函数体 22 inner() 23 #由于第二种调用方式 和 第一种调用方式实现的效果是一样的。所以就满足了 装饰器的两个条件: 24 #(1)不改变被调用函数test()的源代码 25 #(2) 不改变被调用函数test()的调用方式 26 #继续过度:由于第二种方式满足了装饰器调用的两个条件,就可以用 装饰器的一个 语法糖 形式来替换第二种形式, 27 #以便实现简便操作 , 即加一个 @outer .如下面的例子 28 #加语法糖 29 #终极版################################### 30 import time 31 def outer(func): 32 def inner(): 33 print('开始运行被测试函数') 34 start_time = time.time() 35 func() 36 end_time = time.time() 37 print('被测试函数的运行时间是:%s'%(start_time - end_time)) 38 return inner 39 @outer 40 def test(): 41 print('正在运行这个测试函数') 42 time.sleep(2) 43 44 #加了语法糖后,调用方式就简单很多,只需要这样写就好了。语法糖 @outer 就等价于 test =outer(test) 45 test()
运行结果:
1 开始运行被测试函数 2 正在运行这个测试函数 3 被测试函数的运行时间是:-2.000378370285034 4 开始运行被测试函数 5 正在运行这个测试函数 6 被测试函数的运行时间是:-2.0004947185516357 7 开始运行被测试函数 8 正在运行这个测试函数 9 被测试函数的运行时间是:-2.0002481937408447