现在就来详细讲解一下python的装饰器!
装饰器:装饰器本身是用来是为一个函数是实现新的功能,并且不改变原函数的代码以及调用方式。
装饰器的理解比较难,装饰器的使用用到了闭包,闭包是学习装饰器的基础。所以先看闭包:
闭包
# coding:utf-8
def fun():
def fun1():
print("i am fun1")
return fun1 #这个地方返回fun1的内存地址
if __name__ == '__main__':
f = fun() #f保存了fun1的内存地址
f() #执行f函数就是执行fun1函数
print("函数fun1的内存地址为:",f)
上面代码中,我们首先执行f = fun()。这里我们先走fun函数,fun()内部有fun1()函数,最终返回fun1的内存地址。
函数定义完后,在全局把fun()赋值给变量f,此时f中拿到的是fun1的内存地址,这时候你可以把f看成是fun1,进行f()操作相当于调用fun1(),所以上面代码的输出结果为:
这时候你可能会问,f就是fun1,为什么不能直接调用fun1呢,我们可以试试能不能调用:
因为fun1()是在全局的函数fun()里面定义的,全局情况下只能调用fun(),不可以直接调用fun()里面的函数。
我们要想在全局情况下调用“全局函数”内部定义的函数,就必须令该全局函数返回“内部函数”的内存地址,然后将该内存地址赋值给一个变量,通过调用这个变量来实现“全局调用内部函数”,而此时,这个“内部的函数”就称为“闭包”。
下面就来说说装饰器
第一种:不带参数的装饰器:
这里我们先定义了一个装饰器Fun(),而Fun函数里面的fun1函数就是一个闭包。当我们在函数Main_fun定义前加上@Fun时,这个语句相当于:Main_fun = Fun(Main_fun)。也就是说,我们在进行不带参数的装饰器的调用时,相当于把下面的函数名当做参数传给了@后面的函数,@Fun也就相当于执行了Fun(Main_fun)。后面就好理解了:Fun()函数返回了fun1函数的内存地址,下面的Main_fun()其实就调用了“闭包”fun1(),进行了fun1()函数里面的操作。
# coding:utf-8
def Fun(func):
def fun1(*args):
print("我是闭包函数哦~",args)
func() #这个函数是传进来的函数,与外界无关
return fun1 # 这个地方返回fun1的内存地址
@Fun # 等价于 Main_fun = fun(Main_fun)
def Main_fun():
print("我是被装饰的函数哦~")
if __name__ == '__main__':
Main_fun() #运行这个函数就直接跳到闭包里面去了。这个函数是否执行,取决于闭包里面是否调用
Main_fun("我是参数") #传递的参数会被带到闭包里面去接接收参数。
运行结果:
第二种:带参数的装饰器
运行结果:
这里需要注意的是:如果要返回函数的话,带参数的装饰器就要写三层内嵌函数。
带参数的装饰器的具体执行过程分为两步:首先执行Dec(‘QQ’),不管中间过程,Dec函数返回的是函数outer的内存地址,此时就变成了@outer,按照“不带参数的装饰器”的调用过程我们知道,此时outer将函数func2的名称当做是参数执行outer里面的函数inner()。另外我们还需要注意:现在inner里不仅有func2,还有Dec本身所携带的参数’QQ’。
此外:打印出来的"i’m inner"是在判定if type == 'QQ’后直接执行的;而"i’m func2"是inner()函数执行“outer函数所带的参数”调用的结果,也就是说inner函数最后调用了“outer函数所带的参数”func并执行了它,换句话讲,inner就是一个闭包。