在讲装饰器的内容之前,我们先来看这样一个实例,假设你的老板现在对如下代码,实现在函数输出之前输出before,函数执行之后输出after这样的需求
def func():
print("我是func函数")
value=(11,22,33,44)
return value
result=func()
print(result)
相信有很多小伙伴的思路是这样的,在函数执行的语句之前加print(”before“),在执行之后的语句加print(”after“)
代码被修改为如下所示:
def func():
print("before")
print("我是func函数")
value=(11,22,33,44)
print("after")
return value
result=func()
print(result)
我们现在让代码运行,看看输出结果是什么:
before
我是func函数
after
(11, 22, 33, 44)
咦!确实功能被很好的实现了,但如果是我,面对这样的需求我会这样去实现。
def outer(origin):
def inner():
print("before")
print("我是func函数")
res=origin()
print("after")
return res
return inner
def func():
print("我是func函数")
value=(11,22,33,44)
return value
func=outer(func)
result=func()
print(result)
我的思路也很好的实现了功能,但肯定有人不服,觉得我的这种思路太繁琐了,不好理解,代码数量太多等等。
我是func函数
(11, 22, 33, 44)
听到这些话,我心想好像挺有道理的,于是,我去查资料,翻书等等,我意外地发现了python中支持一种特殊语法:
在某个函数上方使用:@函数名
def xxx():
pass
python内部会自动执行函数名(xxx),执行之后,再将结果赋值给xxx. xxx=函数名(xxx)
那么对于我思路的代码,我就可以进行优化,优化后为下图所示:
def outer(origin):
def inner():
print("before")
print("我是func函数")
res=origin()
print("after")
return res
return inner
@outer# func=outer(func)
def func():
print("我是func函数")
value=(11,22,33,44)
return value
result=func()
print(result)
before
我是func函数
我是func函数
after
(11, 22, 33, 44)
但优化之后,还是有小伙伴不服,仍然觉得我的代码还是很繁琐,那我就只好改题目了!!
现在我们将func函数增加到三个,对于刚开始使用print函数的小伙伴仍然觉得自己的方法还是挺好,大不了多写几句啊,根据我的思路是这样实现的:
只需要在每个函数前面加@函数名
def outer(origin):
def inner():
print("before")
res=origin()
print("after")
return res
return inner
@outer
def func():
print("我是func函数")
value=(11,22,33,44)
return value
@outer
def func1():
print("我是func函数")
value=(11,22,33,44)
return value
@outer
def func2():
print("我是func函数")
value=(11,22,33,44)
return value
func()
func1()
func2()
before
我是func函数
after
before
我是func函数
after
before
我是func函数
after
现在是不是觉得我的这种思路好像有点优势了?其实各有各的好,总结一下:应用场景,多时,我的思路好,少时,你的思路好,不能改变源码,就只能使用我的了。
那么我的思路到底是如何实现的呢?现在我们对上代码进行分析:
def outer(origin):#origin等价于实参func,只不过该实参是函数
def inner():
print("before")
print("我是func函数")
res=origin()#内层函数引用了外层函数的变量,对origin函数进行调用
#相当于调用最开始的func函数
print("after")
return res#由于我们调用的func函数有返回值,所以这里需要将他的返回值返回去
return inner#这里相当于把inner函数和开始定义的func函数进行打包一起返回给下面的函数func
def func():
print("我是func函数")
value=(11,22,33,44)
return value
func=outer(func)#注意func不是函数调用,是赋值语句,outer()才是对outer函数的调用(可简化为@函数名)
#既然它们相等,是不是相当于func等于outer函数调用的结果,也就是等于inner函数
#outer(func)实际是把我们最开始定义的func当作参数传递给outer函数
result=func()#对func函数进行调用,注意这里的func函数并不是我们最先定义的func函数
#而是经过赋值之后的,实际上它也就是inner函数,result作为接收inner函数的返回值
print(result)
听我这样解释了一大堆之后,有些小伙伴想必已经猜到了这并不是我的思路就是使用装饰器,还有一部分小伙伴处于懵逼状态,装饰器是什么鬼?
那么下面我们就来学习有关装饰器的内容
装饰器的概念:
装饰器本质:
是一个python函数,它可以让其他函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数对象。
装饰器的定义:
是一个闭包函数,把一个函数当做另外一个函数的参数传递,返回一个替代的函数,返回函数中的函数。
装饰器的实现原理:
基于@语法和函数闭包,将原函数封装在闭包中,然后将函数赋值给一个新的函数(内层函数),执行函数时,在内层函数中执行闭包中的原函数。
实现效果:
可以在不改变原函数内部代码和调用方式的情况下,实现在函数执行和执行的拓展功能。 使用场景:多个函数系统统一在执行前后自定义一些功能。
说到这里有些小伙伴可能会有点😵,什么叫闭包函数?
闭包函数的定义:
假设有个嵌套函数,如果内函数使用了外函数的局部变量,并且外函数把内函数返回出来的过程叫闭包,里面的内函数叫闭包函数。
闭包函数的特点:
如果在闭包函数中使用了某个局部变量。这个局部变量的空间会被系统保存至闭包结束。
闭包的用途:
可以读取到整个父级作用域中的变量,可以让这些变量始终保持在内存中。
我们现在通过一个示例具体分析一下:
def names(name):#names为闭包函数
def age():
age=int(input("请输入年龄"))
print(f"{name}今年{age}")#内层函数引用外层函数变量
return age#外层函数返回内层函数地址
person=names("易烊千玺")#用函数person来接受内层函数的地址
person()#调用person函数<==>调用内层函数
输出结果如下所示:
请输入年龄22
易烊千玺今年22
优化代码使它支持参数:
上面我们提到的示例是不含参数的,显然,它只适用于绝少数的情况,那么,对于含参情况下,又是怎么样呢?
含有一个参数:
before
我是func函数
after
before
我是func函数
after
before
我是func函数
after
但每个函数如果想实现不同个数的参数传递呢?回想一下我们之前在函数章节是不是学过不定长参数这个知识点啊?(忘记的小伙伴回去复习一下),当函数的参数不确定的时候,我们可以使用*args(接收任意数量的位置实参)和**kwargs(接收任意数量的关键字实参)。
经过修改之后,我们给函数传递不同数量的参数:
含多个参数:
def outer(origin):
def inner(*args,**kwargs):
print("before")
res=origin(*args,**kwargs)
print("after")
return res
return inner
@outer
def func(a1,a2,a3):
print("我是func函数")
value=(11,22,33,44)
return value
@outer
def func1(a2,b2):
print("我是func函数")
value=(11,22,33,44)
return value
@outer
def func2(a2,b2,c2):
print("我是func函数")
value=(11,22,33,44)
return value
func(1,23,a3=12)
func1(2,10)
func2(a2=1,b2=3,c2="你好")
before
我是func函数
after
before
我是func函数
after
before
我是func函数
after
程序依然可以很好的运行。
注意:参数在进行传递的时候,实参形参数量必须相同,关键字参数传递,实参关键字和形参关键字必须对应起来。
那么装饰器应该怎么书写呢?
def xx1(被装饰函数):
def xx2(如果被装饰函数有参数那么输入):
代码块
被装饰函数(如果被装饰函数有参数那么输入)
代码块
# 如果被装饰函数中含有return则需要返回被装饰函数
# 没有则不需要
return xx2