装饰器-初级
在不改变原有函数逻辑功能的基础上,为函数添加新的逻辑功能。使代码可读性更高、结构更加清晰、冗余度更低
- 简介
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)