装饰器
1.什么是装饰器
装饰器本质其实就是python函数或python类(因为可以通过函数和类实现装饰器)
装饰器本身是闭包函数的一种应用,因此也具备闭包函数的三个特性;
- 必须是嵌套函数
- 内层函数使用了外层函数中的非全局变量;可以是非上一层函数,对层级没要求
- 外层函数返回下一层函数的引用;必须是下一层函数,对层级有要求
2. 装饰器的作用
1.在不改变源代码及调用方式的前提下,为其增加新的功能
3. 装饰器中的小知识点拆分
1.函数装饰器之变量的不释放
一般来说,函数执行完了,局部的整个名称空间都会被回收。但是如果名称空间中的某一个变量被其他地方引用到了,该变量所属的整个名称空间都不会被回收,而不是单指这个变量。另外外函数返回了内函数的地址引用给了一个全局变量,又因为全局变量在程序运行期间都不会被释放,所以全局变量(也就是内部函数引用)中所引用的外函数的临时变量不会被释放,相当于成了一个全局变量
2.装饰器嵌套嵌套三层的原因
一般来说,一个函数体中需要的传参是直接通过外部传递实参,函数类定义形参进行接收。但是会存在一种情况就是说,函数的参数是固定的,不允许为其添加新的参数,但是这个函数体又需要传参,我们就可以采用闭包的方式传参,闭包其实就是一种新的传参方式
遇到的问题:当函数体需要传参时,但是传参列表不能改动时(如下图)
下方报错原因:
如上图,首先内层的形参列表(*args,**kwargs)是要与被装饰函数test1的列表进行适应的,如果写在里面,那么将会报错
这种情况下,就采用传参的第二种方式,通过闭包进行传参(第一种是直接在当前函数传入形参)
那么我包第二层count_time(func),然后传参可不可以,结果如下,由于语法糖的特性,导致这里的参数也不能改
最终,只能选择包第三层,而第三层是能够接受所有参数的,之所以一二层不穿是因为固定用法,第三层就没这个限制了
3.@语法糖的作用
4. functools.wraps修复被装饰函数的结构问题
https://zhuanlan.zhihu.com/p/45535784
4. 类装饰器
类装饰器的应用:https://www.cnblogs.com/hiyang/p/12634734.html
5.多个装饰器装饰一个函数
加载顺序:从下到上
def deco1(func1):
def wrapper1(*args, **kwargs):
print('正在运行wrapper1第一行')
res1 = func1(*args, **kwargs)
print('正在运行wrapper1最后一行————————————')
return res1
return wrapper1
def deco2(func2):
def wrapper2(*args, **kwargs):
print('正在运行wrapper2第一行')
res2 = func2(*args, **kwargs)
print('正在运行wrapper2最后一行————————————')
return res2
return wrapper2
def deco3(func3):
def wrapper3(*args, **kwargs):
print('正在运行wrapper3第一行')
res3 = func3(*args, **kwargs)
print('正在运行wrapper3最后一行————————————')
return res3
return wrapper3
# 装饰器的加载顺序是从下往上,最近的先加载
@deco1 # cc=deco1(wrapper2的内存地址) ==>此时cc得到的是wrapper1的内存地址
@deco2 # cc=deco2(wrapper3的内存地址) ==>此时cc得到的是wrapper2的内存地址
@deco3 # cc=deco3(cc) ==>此时cc得到的是wrapper3的内存地址
def cc():
print('执行主代码')
# 可以直接输出cc的地址来确定
print(cc) # <function deco1.<locals>.wrapper1 at 0x00000256F6B6A700>
# 执行顺序是从上往下,这个不用特别记住;由于加载顺序的原因,cc现在指向wrapper1,调用函数想当于调用wrapper1(),而wrapper1需要wrapper2的地址,所以会先往下执行,以此类推
cc()
运行结果
<function deco1.<locals>.wrapper1 at 0x0000024DFA37A700>
正在运行wrapper1第一行
正在运行wrapper2第一行
正在运行wrapper3第一行
执行主代码
正在运行wrapper3最后一行————————————
正在运行wrapper2最后一行————————————
正在运行wrapper1最后一行————————————
调用顺序图示例
本文来自博客园,作者:中州韵,转载请注明原文链接:https://www.cnblogs.com/zhongzhouyun/p/14967840.html