十九、python沉淀之路--装饰器

一、实现装饰器的预备知识

装饰器 = 高阶函数 + 函数嵌套 + 闭包

1、高价函数定义:

    1.函数接收的参数是一个函数名
    2.函数的返回值是一个函数名
    3.满足上述条件任意一个,都可称之为高阶函数

例1:铺垫

 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)
View Code

例2

 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()
View Code
1 正在执行高阶函数
2 <function fn at 0x0000017065C999D8>
3 这是被调用函数
4 正在执行高阶函数
5 <function fn at 0x0000017065C999D8>
6 这是被调用函数
View Code

升级版 :将调用函数test(func)的返回值赋值给一个与被调用函数fn()同名的变量fn,最后实现的效果就是
1)不改变被调用函数fn()的源代码
2) 不改变被调用函数fn()的调用方式

 例3  这种情况出现的结果是 多运行了一行

 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()
View Code
1 开始执行调用函数
2 这是被调用函数
3 这是被调用函数
View Code

例4

 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
View Code
1 正在执行高阶函数
2 这是被调用函数
3 被调用函数执行完毕
View Code
即使赋了一个值,也还是解决不了多打印一行的结果。所以单层函数解决不了这个问题

高阶函数总结:
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
原文地址:https://www.cnblogs.com/jianguo221/p/8983729.html