python装饰器
函数闭包:函数内部嵌套函数
闭包举例:
def w():
print("闪开,我要开始装逼了")
def f():
print("hello world!")
return f
# 调用:
res = w()
res()
# 或w()()
闭包就是在一个函数内部再定义一个函数,外部的函数返回内部函数名。这样,仅仅调用外部函数得到的就只是执行非内部函数部分的函数体,并返回一个内部函数名。因作用域的关系,外部函数体以外是调用不到内部函数的。所以为了能够在外部调用到,外部函数体返回的是内部函数的函数名而不加括号,这样就可以在外部调用内部函数时更加灵活(如向内部函数传参)。
装饰器原理:闭包的内部函数调用被装饰的函数
装饰器实际上也是一个闭包,在闭包的内部函数中调用被封装的函数,并添加需要装饰的功能。这样,一个装饰器函数就写好了,然后@符号加外部函数名就是装饰器。定义被装饰函数时,将装饰器写在被装饰函数上面就行了。
# 定义装饰器
def zhuangshiqi(fun):
print("装饰一下")
def inner():
print("在这里添加一些装饰内容")
fun()
return inner
# 使用装饰器
@zhuangshiqi
def beizhuangshihanshu():
print("被装饰函数的内容")
# 调用被装饰后的函数
beizhuangshihanshu()
由此可以发现,当python解释器执行到@zhuangshiqi时,就开始进行装饰了,相当于执行了如下代码
beizhuangshihanshu = zhuangshiqi(beizhuangshihanshu)
两个装饰器执行流程和装饰结果
def zhaungshiqi_01(fun_01):
print('----a----')
def inner_01():
print('----1----')
return '<b>' + fun_01() + '</b>'
return inner
def zhuangshiqi_02(fun_02):
print('----b----')
def inner_02():
print('----2----')
return '<i>' + fun_02() + '</i>'
return inner
@zhaungshiqi_01
@zhuangshiqi_02
def test():
print('----c----')
print('----3----')
return 'hello world'
ret = test()
print(ret)
先看执行结果:
----b----
----a----
----1----
----2----
----c----
----3----
<b><i>hello python decorator</i></b>
**实现顺序:**第二个装饰器@zhuangshiqi_02先进行装饰 ——> 第一个装饰器@zhaungshiqi_01再进行装饰 ——>执行第一个装饰器内部函数 ——>执行第二个装饰器内部函数
原因分析:
1、由内及外:当代码运行到第一个装饰器@zhaungshiqi_01时,本来应该是对下面的函数进行装饰,而此时代码往下走后紧接着的并不是一个函数,而是又一个装饰器。这时候装饰器@zhaungshiqi_01暂停执行,接着执行下面的装饰器@zhuangshiqi_02。然后被装饰函数test传入到装饰器函数zhuangshiqi_02中,执行zhuangshiqi_02装饰内容“print(‘----b----’)”,zhuangshiqi_02在完成装饰后,将已经被装饰过一次的test函数传给外层的装饰器函数zhaungshiqi_01,执行zhaungshiqi_01装饰内容“print(‘----a----’)”
2、再由外及内:根据由内及外原则,此时的test是位于最外层装饰器函数zhaungshiqi_01的内部函数inner_01()处,执行"print(‘----1----’)",然后调用fun_01()。而此时的fun_01()实际上是inner_02(),即先执行“print(‘----2----’)”,再调用fun_02()。fun_02()才是test(),所以执行“print(‘----c----’)”、“print(‘----3----’)”、“return ‘hello python decorator’”。层层调用之后,结果为b标签里面套i标签,再套hello world
对有参数的函数进行装饰:内部函数参数与被装饰函数参数保持一致
我们应该知道,装饰器函数的外部函数是用来接收被装饰函数名的,调用被装饰函数是在内部函数。那么如果被装饰函数有参数,也应该是在调用的时候传给它。所以,装饰器函数之内部函数的参数应该与被装饰函数的参数保持一致。当然,这里说的参数保持一致是指形参的数量、类型等保持一致。
def say_hi(fun):
def inner(name):
print("你好!")
fun(name)
return inner
@say_hi
def short_name(name):
print(f"你就是小{name[0]}吧")
short_name("王富贵")
还记得本文开头提到的装饰器原理吗?既然short_name = say_hi(short_name),那么short_name(name)自然就等于say_hi(short_name)(name),即被装饰器的参数先传给了装饰器函数的内部函数inner(),然后再由内部函数将参数传给被调用的函数。
注意:如果被装饰的函数有返回值,那么装饰器函数的内部函数也应该先接收再返回
def zhuangshiqi(fun):
print("装饰一下")
def inner(n):
num = fun(n)
return num
return inner
@zhuangshiqi
def jiecheng(n):
assert n >= 0 and type(n) == int,"不能阶乘"
if n == 1 or n == 0:
return 1
else:
return n * jiecheng(n-1)
引申:装饰器的使用频率很高,而被装饰函数的参数数量不一怎么破?可变参数*args,关键字参数**kwargs了解一下
def zhuangshiqi(fun):
print("装饰一下")
def inner(*args,**kwargs):
num = fun(*args,**kwargs)
return num
return inner