python高级 之(二) --- 类装饰器

 装饰器-初级

  在不改变原有函数逻辑功能的基础上,为函数添加新的逻辑功能。使代码可读性更高、结构更加清晰、冗余度更低

  • 简介
 1 """
 2 闭包:  函数嵌套的格式就是闭包。写装饰器的闭包是外层函数的返回值就是内层函数
 3 装饰器:一种特殊的闭包 加上 语法糖[@语法]组成的
 4 其作用:在不修改原有功能的基础上,为该功能添加其他新的需求。不管在函数中添加多少装饰,函数的名字没有变化
 5 
 6 在函数中的使用
 7     装饰器的特殊闭包:在外层函数中声明一个内层函数,外等函数的返回值是内层函数
 8     外层函数的参数是:func 接受是给哪一个函数添加装饰
 9         def out(func):   给nomal函数装饰时,此时 func === nomal()  |   给diff函数装饰时,此时 func === diff()
10             def inner():
11                 pass     装饰器的新增功能是 写在内层函数中的,写在外层函数没有效果
12             return inner
13 
14     需添加装饰的函数:在普通函数添加装饰器的语法糖,@特殊闭包的外层函数名
15         @out
16         def nomal():
17             pass
18         @out
19         def diff()
20             pass
21 """
22 # 特殊的闭包
23 def outter():
24     print("这是一个外部函数")
25     def inner():
26         # 装饰器的新增功能是 写在内层函数中的
27         print("这是一个内部函数")
28     return inner    # 外部函数的返回值是内部函数
29 res = outter()
30 print('res =',res)  # res指向的是内部函数的内存地址。
31 # 执行结果:这是一个外部函数 res = <function outter.<locals>.inner at 0x1027e88c8>
32 res() 
33 # 执行结果 :这是一个内部函数。 调用res函数等价于 在outter内部直接调用innner函数
34 
35 """======================================================================"""
  • 实例
 1 # 在不改变原有函数的基础上,为函数增加新的功能。
 2 
 3 # 装饰器的闭包,实现功能是:新增计算该功能函数的执行时间
 4 def get_time(func): #  func的作用是 明确为哪一个函数添加装饰器;为哪个函数添加就代表哪一个函数
 5     def inner():
 6         import time
 7         start_time = time.time()
 8         # 执行被装饰的功能函数
 9         func()
10         end_time = time.time()
11         print("消耗的时间为:",end_time - start_time)
12     return inner
13 
14 @get_time
15 def print_table():
16     for row in range(1,10):
17         for col in range(1,row + 1):
18             print(col, "*" ,row, "=", row*col, end="	")
19         print()
20 
21 # 不管给该功能函数添加了多少装饰,执行的时候还是调用该函数的名字
22 print_table()
 1 """
 2 1 * 1 = 1    
 3 1 * 2 = 2    2 * 2 = 4    
 4 1 * 3 = 3    2 * 3 = 6    3 * 3 = 9    
 5 1 * 4 = 4    2 * 4 = 8    3 * 4 = 12    4 * 4 = 16    
 6 1 * 5 = 5    2 * 5 = 10    3 * 5 = 15    4 * 5 = 20    5 * 5 = 25    
 7 1 * 6 = 6    2 * 6 = 12    3 * 6 = 18    4 * 6 = 24    5 * 6 = 30    6 * 6 = 36    
 8 1 * 7 = 7    2 * 7 = 14    3 * 7 = 21    4 * 7 = 28    5 * 7 = 35    6 * 7 = 42    7 * 7 = 49    
 9 1 * 8 = 8    2 * 8 = 16    3 * 8 = 24    4 * 8 = 32    5 * 8 = 40    6 * 8 = 48    7 * 8 = 56    8 * 8 = 64    
10 1 * 9 = 9    2 * 9 = 18    3 * 9 = 27    4 * 9 = 36    5 * 9 = 45    6 * 9 = 54    7 * 9 = 63    8 * 9 = 72    9 * 9 = 81    
11 消耗的时间为: 0.0011630058288574219
12 """
 1 # 在不改变原有函数的基础上,为函数增加新的功能。
 2 
 3 # 装饰器的闭包,实现功能是:新增计算该功能函数的执行时间
 4 def get_time(func):   # func的作用是明确给哪一个函数添加装饰器
 5     def inner():
 6         import time
 7         start_time = time.time()
 8         # 执行被装饰的功能函数
 9         func()
10         end_time = time.time()
11         print("消耗的时间为:",end_time - start_time)
12     return inner
13 
14 """九九乘法表"""
15 @get_time
16 def print_table():
17     for row in range(1,10):
18         for col in range(1,row + 1):
19             print(col, "*" ,row, "=", row*col, end="	")
20         print()
21 # 不管给该功能函数添加了多少装饰,执行的时候还是调用该函数的名字
22 print_table()
23 """九九加法表"""
24 @get_time
25 def print_add_table():
26     for row in range(1,10):
27         for col in range(1,row + 1):
28             print(col, "+" ,row, "=", row+col, end="	")
29         print()
30 print_add_table()
多个函数添加同一个装饰器 

装饰器的执行流程

  • 基本流程
 1 def get_time(func):   # func的作用是明确给哪一个函数添加装饰器
 2     print("这是装饰器的外层函数。在添加@语法糖时执行,且只会被执行一次")
 3     def inner():
 4         import time
 5         start_time = time.time()
 6         func()    # 执行被装饰的功能函数
 7         end_time = time.time()
 8         print("消耗的时间为:",end_time - start_time)
 9     return inner
10 """九九加法表"""
11 @get_time
12 def print_add_table():
13     for row in range(1,10):
14         for col in range(1,row + 1):
15             print(col, "+" ,row, "=", row+col, end="	")
16         print()
17 print_add_table()
18 print_add_table() # 无论调用几次被装饰的函数,装饰器的外层函数只会被执行一次
19 """
20 为什么在装饰器中增加的新功能要在内层函数中添加?
21     因为在装饰器函数中 无论调用几次被装饰的函数,
22     装饰器外层函数的语句只会被执行一次,
23     第二次调用被装饰函数时不会执行装饰器外层函数语句
24     
25 当语法糖添加装饰器的时候,内部执行流程:
26     1。当使用语法糖给当前函数添加装饰器的时候[添加@时],装饰器特殊闭包的外层函数就已经被调用执行了
27        注意:外层函数的执行会有一个结果的返回,这个结果就是其中的内层函数
28        
29     2。为哪一个功能函数添加的装饰器 就使用该功能函数名字作为变量名,用于接收外层函数执行的返回结果(即内层函数)
30        此时 print_add_table = get_time(print_add_table) # 装饰器函数的参数 是 被装饰的函数
31        
32     3。现在此时 print_add_table 代表的是:装饰器特殊闭包中的内层函数;装饰器的参数func 代表的是被装饰的方法函数
33 """
  • 装饰器的完善
 1 def get_time(func):          # func的作用是明确给哪一个函数添加装饰器
 2     def inner(*values,**kwargs):      # 带参数的功能函数添加装饰器 所传的值
 3         import time
 4         start_time = time.time()
 5         res = func(*values,**kwargs)  # 执行被装饰的功能函数; values 传的参数
 6         end_time = time.time()
 7         print("消耗的时间为:",end_time - start_time)
 8         return res
 9     return inner
10 """
11 计算两个数求和功能的执行时间。即带参数的功能函数 添加装饰器
12 """
13 @get_time
14 def add(a,b):
15     return a + b
16 res = add(12,34)
17 print(res)
 1 def one_oper(func):
 2     flag = False  # 设置一个标记位。让装饰器只能返回一次结果
 3     def inner(*value,**kwargs):
 4         nonlocal flag
 5         if not flag:
 6             res = func(*value,**kwargs)
 7             flag = True
 8             return res
 9         return None
10     return inner
11 @one_oper
12 def add(a,b):
13     return a + b
14 
15 
16 res = add(12,34)
17 print(res) # 结果:46
18 res = add(34,12)
19 print(res) # 结果:None
唯一输出

 多个装饰器的执行流程

  • 代码
 1 """
 2 一个装饰器可以装饰多个功能函数
 3 一个功能函数可以被多个装饰器装饰 执行顺序采用就近原则执行[先执行距离被装饰函数近的]
 4 """
 5 
 6 def outer1(func):
 7     print("这是第一个装饰器的外部函数")
 8     def inner1(*value,**kwargs):
 9         print("第一个装饰器的内部函数")
10         res = func(*value,**kwargs)
11         if res != None:
12             return res
13     return inner1
14 def outer2(func):
15     print("这是第二个装饰器的外部函数")
16     def inner2(*value,**kwargs):
17         print("第二个装饰器的内部函数")
18         res = func(*value,**kwargs)
19         if res != None:
20             return res
21     return inner2
22 def outer3(func):
23     print("这是第三个装饰器的外部函数")
24     def inner3(*value,**kwargs):
25         print("第三个装饰器的内部函数")
26         res = func(*value,**kwargs)
27         if res != None:
28             return res
29     return inner3
30 
31 @outer1
32 @outer2
33 @outer3
34 def show():
35     print("演示添加多个装饰器的功能")
36     # 被添加装饰器的功能函数只会被执行一次,而且是在最后才执行
37 show()
  • 结果
 1 """
 2  执行结果:
 3     这是第三个装饰器的外部函数
 4     这是第二个装饰器的外部函数
 5     这是第一个装饰器的外部函数
 6     第一个装饰器的内部函数
 7     第二个装饰器的内部函数
 8     第三个装饰器的内部函数
 9     演示添加多个装饰器的功能
10  执行流程:
11     @outer3 此时执行outer3外层函数
12         ---> show = outer3(show)
13         show === inner3此内部函数
14     @outer2 此时执行outer2外层函数
15         ---> show = outer2(show)[注意:执行上一个装饰器的时候,show已经被更改数据了]
16              此时型参func=show=innner3  当作实际参数传递给show ===> 即上一个inner3
17         这个时候被赋值的变量show又拥有了新值 inner2
18     @outer1
19         ---> show = outer1(show)[注意:执行上一个装饰器的时候,show已经被更改数据了]
20              此时型参func=show=innner2  当作实际参数传递给show ===> 即上一个inner2
21         这个时候被赋值的变量show又拥有了新值 inner1
22     
23     所有的装饰器走完之后,此时的show === inner1
24         inner1执行完后,此时的show === inner2
25         inner2执行完后,此时的show === inner3
26         inner3执行完后,此时的show === 被装饰的功能函数
27 """



装饰器-进阶

带参数的函数装饰器

  之前的例子中装饰器是不能接收参数的,其用法只能适用于一些简单的场景。不传参的装饰器,只能对被装饰函数执行固定逻辑。

  装饰器本身就是一个函数,不传参数的装饰器,只能对被装饰的函数执行固定的逻辑,会大大限制其能力。

  若要用两个内容大体一致,但是某些地方不同的逻辑,若不能传参数的话,就需要写两个装饰器。

 1 # 装饰器实现传参需要两层嵌套
 2 def say_hello(contry):  # 外层函数的型参 接收 装饰器传来的实参
 3     def wrapper(func):  # 中层函数的型参 接收 被添加装饰器函数
 4         def deco(*args, **kwargs):  # 内层函数的型参 接收 被添加装饰器的带参数功能函数所传的实参
 5             if contry == "china":
 6                 print("你好!")
 7             elif contry == "america":
 8                 print('hello.')
 9             else:
10                 return
11             func(*args, **kwargs)   # 真正执行函数的地方。
12         return deco
13     return wrapper
14 
15 @say_hello("america")
16 def american():
17     print("I am from America。")
18 @say_hello("china")
19 def chinese():
20     print("我来自中国。")
21 
22 american()
23 chinese()
24 """
25 结果:
26     hello.
27     I am from America。
28     你好!
29     我来自中国。
30 """



装饰器-高级

不带参数的类装饰器

 1 """
 2 基于类实现的装饰器,需要两个类方法来实现
 3     __init__ : 接收被装饰的函数
 4     __call__ : 实现装饰器的逻辑
 5 """
 6 class Text(object):
 7     def __init__(self, func):            # 接收被装饰的函数
 8         self.func = func
 9     def __call__(self, *args, **kwargs): # 实现装饰器的逻辑
10         print("正在被装饰的函数是:{func}".format(func=self.func.__name__))
11         return self.func(*args, **kwargs)
12 @Text
13 def func(something):
14     print("函数的参数是:{}!".format(something))
15 
16 func("hello")
17 """
18 结果:
19     正在被装饰的函数是:func
20     函数的参数是:hello!
21 """

带有参数的类装饰器

 1 """
 2 带参数的类装饰器
 3     __init__ :不再接收被装饰的函数,而是接收传入的参数。
 4     __call__ :接收被装饰函数,实现装饰逻辑。
 5 """
 6 class logger(object):
 7     def __init__(self, level='INFO'):  # 接收装饰器传入的参数
 8         self.level = level
 9     def __call__(self, func):          # 接受被装饰的函数
10         def wrapper(*args, **kwargs):
11             print("[{level}]: the function {func}() is running..."
12               .format(level=self.level, func=func.__name__))
13             func(*args, **kwargs)      # 真正执行被装饰的函数的语句
14         return wrapper                 # 返回内层函数
15 
16 @logger(level='WARNING')
17 def say(something):
18     print("say {}!".format(something))
19 say("hello")
20 @logger(level='DEBUG')
21 def say(something):
22     print("say {}!".format(something))
23 say("hello")
24 """
25 结果: 
26     [WARNING]: the function say() is running...
27     say hello!
28     [DEBUG]: the function say() is running...
29     say hello!
30 """



系统提供的装饰器

  Python语言本身提供的property内建装饰器,通常存在于类中的方法属性化。详情见 类的方法属性化 笔记整理

 1 """
 2 Python语言本身提供的property内建装饰器,通常存在于类中的方法属性化
 3 可以将一个方法函数定义成一个属性,属性的值就是该函数return的内容。
 4 """
 5 # 用@property装饰过的函数,会将一个函数定义成一个属性,属性的值就是该函数return的内容。即方法属性化
 6 class Student(object):
 7     def __init__(self, name):
 8         self.name = name
 9         self.name = None
10     @property
11     def age(self):
12         return self._age
13     @age.setter
14     def age(self, value):
15         if not isinstance(value, int):
16             raise ValueError('输入不合法:年龄必须为数值!')
17         if not 0 < value < 100:
18             raise ValueError('输入不合法:年龄范围必须0-100')
19         self._age=value
20     @age.deleter
21     def age(self):
22         del self._age
23 XiaoMing = Student("小明")
24 
25 # 设置属性
26 XiaoMing.age = 25
27 # 查询属性
28 age = XiaoMing.age
29 print(age)
30 # 删除属性。删除后再去查询该属性就会报错
31 del XiaoMing.age
32 age = XiaoMing.age
33 print(age)
生如逆旅 一苇以航
原文地址:https://www.cnblogs.com/TMMM/p/11395710.html