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

时间:2022-02-17 03:11:41

装饰器-初级

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

  • 简介
 """
闭包: 函数嵌套的格式就是闭包。写装饰器的闭包是外层函数的返回值就是内层函数
装饰器:一种特殊的闭包 加上 语法糖[@语法]组成的
其作用:在不修改原有功能的基础上,为该功能添加其他新的需求。不管在函数中添加多少装饰,函数的名字没有变化 在函数中的使用
装饰器的特殊闭包:在外层函数中声明一个内层函数,外等函数的返回值是内层函数
外层函数的参数是:func 接受是给哪一个函数添加装饰
def out(func): 给nomal函数装饰时,此时 func === nomal() | 给diff函数装饰时,此时 func === diff()
def inner():
pass 装饰器的新增功能是 写在内层函数中的,写在外层函数没有效果
return inner 需添加装饰的函数:在普通函数添加装饰器的语法糖,@特殊闭包的外层函数名
@out
def nomal():
pass
@out
def diff()
pass
"""
# 特殊的闭包
def outter():
print("这是一个外部函数")
def inner():
# 装饰器的新增功能是 写在内层函数中的
print("这是一个内部函数")
return inner # 外部函数的返回值是内部函数
res = outter()
print('res =',res) # res指向的是内部函数的内存地址。
# 执行结果:这是一个外部函数 res = <function outter.<locals>.inner at 0x1027e88c8>
res()
# 执行结果 :这是一个内部函数。 调用res函数等价于 在outter内部直接调用innner函数 """======================================================================"""
  • 实例
 # 在不改变原有函数的基础上,为函数增加新的功能。

 # 装饰器的闭包,实现功能是:新增计算该功能函数的执行时间
def get_time(func): # func的作用是 明确为哪一个函数添加装饰器;为哪个函数添加就代表哪一个函数
def inner():
import time
start_time = time.time()
# 执行被装饰的功能函数
func()
end_time = time.time()
print("消耗的时间为:",end_time - start_time)
return inner @get_time
def print_table():
for row in range(1,10):
for col in range(1,row + 1):
print(col, "*" ,row, "=", row*col, end="\t")
print() # 不管给该功能函数添加了多少装饰,执行的时候还是调用该函数的名字
print_table()
 """
1 * 1 = 1
1 * 2 = 2 2 * 2 = 4
1 * 3 = 3 2 * 3 = 6 3 * 3 = 9
1 * 4 = 4 2 * 4 = 8 3 * 4 = 12 4 * 4 = 16
1 * 5 = 5 2 * 5 = 10 3 * 5 = 15 4 * 5 = 20 5 * 5 = 25
1 * 6 = 6 2 * 6 = 12 3 * 6 = 18 4 * 6 = 24 5 * 6 = 30 6 * 6 = 36
1 * 7 = 7 2 * 7 = 14 3 * 7 = 21 4 * 7 = 28 5 * 7 = 35 6 * 7 = 42 7 * 7 = 49
1 * 8 = 8 2 * 8 = 16 3 * 8 = 24 4 * 8 = 32 5 * 8 = 40 6 * 8 = 48 7 * 8 = 56 8 * 8 = 64
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
消耗的时间为: 0.0011630058288574219
"""
 # 在不改变原有函数的基础上,为函数增加新的功能。

 # 装饰器的闭包,实现功能是:新增计算该功能函数的执行时间
def get_time(func): # func的作用是明确给哪一个函数添加装饰器
def inner():
import time
start_time = time.time()
# 执行被装饰的功能函数
func()
end_time = time.time()
print("消耗的时间为:",end_time - start_time)
return inner """九九乘法表"""
@get_time
def print_table():
for row in range(1,10):
for col in range(1,row + 1):
print(col, "*" ,row, "=", row*col, end="\t")
print()
# 不管给该功能函数添加了多少装饰,执行的时候还是调用该函数的名字
print_table()
"""九九加法表"""
@get_time
def print_add_table():
for row in range(1,10):
for col in range(1,row + 1):
print(col, "+" ,row, "=", row+col, end="\t")
print()
print_add_table()

多个函数添加同一个装饰器

装饰器的执行流程

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

唯一输出

多个装饰器的执行流程

  • 代码
 """
一个装饰器可以装饰多个功能函数
一个功能函数可以被多个装饰器装饰 执行顺序采用就近原则执行[先执行距离被装饰函数近的]
""" def outer1(func):
print("这是第一个装饰器的外部函数")
def inner1(*value,**kwargs):
print("第一个装饰器的内部函数")
res = func(*value,**kwargs)
if res != None:
return res
return inner1
def outer2(func):
print("这是第二个装饰器的外部函数")
def inner2(*value,**kwargs):
print("第二个装饰器的内部函数")
res = func(*value,**kwargs)
if res != None:
return res
return inner2
def outer3(func):
print("这是第三个装饰器的外部函数")
def inner3(*value,**kwargs):
print("第三个装饰器的内部函数")
res = func(*value,**kwargs)
if res != None:
return res
return inner3 @outer1
@outer2
@outer3
def show():
print("演示添加多个装饰器的功能")
# 被添加装饰器的功能函数只会被执行一次,而且是在最后才执行
show()
  • 结果
 """
执行结果:
这是第三个装饰器的外部函数
这是第二个装饰器的外部函数
这是第一个装饰器的外部函数
第一个装饰器的内部函数
第二个装饰器的内部函数
第三个装饰器的内部函数
演示添加多个装饰器的功能
执行流程:
@outer3 此时执行outer3外层函数
---> show = outer3(show)
show === inner3此内部函数
@outer2 此时执行outer2外层函数
---> show = outer2(show)[注意:执行上一个装饰器的时候,show已经被更改数据了]
此时型参func=show=innner3 当作实际参数传递给show ===> 即上一个inner3
这个时候被赋值的变量show又拥有了新值 inner2
@outer1
---> show = outer1(show)[注意:执行上一个装饰器的时候,show已经被更改数据了]
此时型参func=show=innner2 当作实际参数传递给show ===> 即上一个inner2
这个时候被赋值的变量show又拥有了新值 inner1 所有的装饰器走完之后,此时的show === inner1
inner1执行完后,此时的show === inner2
inner2执行完后,此时的show === inner3
inner3执行完后,此时的show === 被装饰的功能函数
"""



装饰器-进阶

带参数的函数装饰器

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

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

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

 # 装饰器实现传参需要两层嵌套
def say_hello(contry): # 外层函数的型参 接收 装饰器传来的实参
def wrapper(func): # 中层函数的型参 接收 被添加装饰器函数
def deco(*args, **kwargs): # 内层函数的型参 接收 被添加装饰器的带参数功能函数所传的实参
if contry == "china":
print("你好!")
elif contry == "america":
print('hello.')
else:
return
func(*args, **kwargs) # 真正执行函数的地方。
return deco
return wrapper @say_hello("america")
def american():
print("I am from America。")
@say_hello("china")
def chinese():
print("我来自中国。") american()
chinese()
"""
结果:
hello.
I am from America。
你好!
我来自中国。
"""



装饰器-高级

不带参数的类装饰器

 """
基于类实现的装饰器,需要两个类方法来实现
__init__ : 接收被装饰的函数
__call__ : 实现装饰器的逻辑
"""
class Text(object):
def __init__(self, func): # 接收被装饰的函数
self.func = func
def __call__(self, *args, **kwargs): # 实现装饰器的逻辑
print("正在被装饰的函数是:{func}".format(func=self.func.__name__))
return self.func(*args, **kwargs)
@Text
def func(something):
print("函数的参数是:{}!".format(something)) func("hello")
"""
结果:
正在被装饰的函数是:func
函数的参数是:hello!
"""

带有参数的类装饰器

 """
带参数的类装饰器
__init__ :不再接收被装饰的函数,而是接收传入的参数。
__call__ :接收被装饰函数,实现装饰逻辑。
"""
class logger(object):
def __init__(self, level='INFO'): # 接收装饰器传入的参数
self.level = level
def __call__(self, func): # 接受被装饰的函数
def wrapper(*args, **kwargs):
print("[{level}]: the function {func}() is running..."\
.format(level=self.level, func=func.__name__))
func(*args, **kwargs) # 真正执行被装饰的函数的语句
return wrapper # 返回内层函数 @logger(level='WARNING')
def say(something):
print("say {}!".format(something))
say("hello")
@logger(level='DEBUG')
def say(something):
print("say {}!".format(something))
say("hello")
"""
结果:
[WARNING]: the function say() is running...
say hello!
[DEBUG]: the function say() is running...
say hello!
"""



系统提供的装饰器

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

 """
Python语言本身提供的property内建装饰器,通常存在于类中的方法属性化
可以将一个方法函数定义成一个属性,属性的值就是该函数return的内容。
"""
# 用@property装饰过的函数,会将一个函数定义成一个属性,属性的值就是该函数return的内容。即方法属性化
class Student(object):
def __init__(self, name):
self.name = name
self.name = None
@property
def age(self):
return self._age
@age.setter
def age(self, value):
if not isinstance(value, int):
raise ValueError('输入不合法:年龄必须为数值!')
if not 0 < value < 100:
raise ValueError('输入不合法:年龄范围必须0-100')
self._age=value
@age.deleter
def age(self):
del self._age
XiaoMing = Student("小明") # 设置属性
XiaoMing.age = 25
# 查询属性
age = XiaoMing.age
print(age)
# 删除属性。删除后再去查询该属性就会报错
del XiaoMing.age
age = XiaoMing.age
print(age)