看装饰器前,必须理解闭包,闭包的解释在另一篇文章里
一句话解释:装饰器就是一个函数
先看一个例子:
推导式:testFunc = dec(testFunc) #有C函数指针基础的比较好理解
def dec(func):
def innerFunc(*args,**kwargs):
print("原来的print语句调用不了了,哇哈哈")
return innerFunc
def testFunc(name):
print("hello:" + name)
testFunc = dec(testFunc)
testFunc("nihao")
dec 是一个普通函数, 参数:func是1个函数对象
内部又定义了个函数,可以随便定义N个函数
最后返回了内部的函数innerFunc
testFunc = dec(testFunc)
这行代码的意思是把testFunc 指向了innerFunc
所以现在调用testFunc 相当于调用了innerFunc
问题来了,参数呢,原来testFunc的参数name ,现在被替换成了 *args, **kwargs ,随便传入什么参数都可以了
把上面的方式用装饰器的方式来写一遍 :
def dec(func):
def innerFunc(*args,**kwargs):
print("原来的print语句调用不了了,哇哈哈")
return innerFunc
@dec
def testFunc(name):
print("hello:" + name)
testFunc("nihao")
在需要被装饰的函数上方添加一个@,后面跟着装饰器.
装饰器是什么?
一个接受*函数对象*,并且返回一个*函数对象*,用于改变原函数的行为(不修改原代码;类似于java中的AOP)
当调用了testFunc() ,实际上是调用了innerFunc().
@dec
def testFunc(name) #相当于testFunc=dec(testFunc)
把innerFunc做些修改
def dec(func):
def innerFunc(*args,**kwargs):
print("args:",args)
func(*args)
return innerFunc
由于闭包的作用域,func 参数被存放在innerFunc.__closure__中的单元对象中 ,所以可以被调用
输出
testFunc("nihao")
#args: ('nihao',)
#hello:nihao
至此,装饰器基础解释完毕
深一些:
装饰器何时被调用 ? 作为模块导入时就会被调用,例子
def dec_again(func):
print("dec_agin")
def innerFunc(*args, **kwargs):
print("dec_again 中的 innerFunc , 参数func:",func)
return innerFunc
def dec(func):
print("dec")
def innerFunc(*args,**kwargs):
print("dec 中的 innerFunc, 参数func:",func)
return innerFunc
@dec_again
@dec
def testFunc(name):
print("hello:" + name)
#注意没有调用testFunc(), 输出
#dec
#dec_agin
这个栗子说明了2个问题:
1.装饰器一开始就会调用,说的大白话一些是因为需要确定函数的执行流程
2.装饰器是可以叠加的,离testFunc最近的装饰器(@dec)先执行,然后@dec_again
之前说过了, testFunc = dec(testFunc)
那么这个栗子由此可以推倒:
testFunc = dec_again(dec(testFunc))
把dec(testFunc)的返回值作为参数传递给dec_again;
testFunc("hi")
#输出
#dec_again 中的 innerFunc , 参数func: <function dec.<locals>.innerFunc at 0x000000000279CD08>
看到了吗 ,当调用testFunc() 实际调用了,dec_again中的innerFunc(),参数是dec返回的innerFunc
接下来说一下,含有参数的装饰器(充分的利用了闭包):
这里的推导式变了,
没有参数的推导式:testFunc = dec(testFunc)
有参数的推导式:testFunc = dec_factory(1,2,3)(testFunc),往下看
def dec_factory(*arg):
print("arg:",arg)
def dec(func): #这个才是装饰器函数, 接受1个函数对象
def innerFunc(*args,**kwargs):
print("我才是真正被执行的函数")
return innerFunc #返回innerFunc , 替换了原来的testFunc
return dec #为什么要返回dec ?
@dec_factory(1,2,3) #需要调用 ,为什么?
def testFunc(name):
print("hello:" + name)
#输出了arg: (1, 2, 3) ,注意没有调用testFunc()
一步步解释:
当有需要有参数的装饰器时 ,需要外面再'包裹'一层函数来接受参数.
前面说了装饰器函数接受一个函数对象,返回一个函数对象,用于改变原函数的行为或流程
所以可以把dec_factory看成一个装饰器工厂,专门用来返回装饰器函数的
这些参数由于闭包的作用域可以随意访问。
@dec_factory(1,2,3) #需要调用 ,为什么?
def testFunc(name):
没有参数时 , @dec 即可。这里为什么需要调用 .
dec_factory 本身并不是装饰器函数,而是一个'工厂'(用来接收参数 ),
他返回的是一个装饰器函数(dec) ,这就是为什么需要调用
@dec_factory(1,2,3) #调用之后返回dec,相当于@dec
def testFunc(name):
进一步推导: testFunc = dec(testFunc)
至此,带有参数的装饰器解释完毕;
最后写一个混合使用的例子:
def print_factory(when):#when 在'before' 'after'中选一个
#普通的内部函数
def print_now(func,*args,**kwargs):
print("print_now-> function , args,kwargs:",
func,args,kwargs)
#一个装饰器函数,在真正执行函数前打印一下
def print_something_first(func):
def innerFunc(*args,**kwargs):
print("innerFunc , 我先打印然后再执行func的调用")
print_now(func,*args,**kwargs)
return func(*args,**kwargs)
return innerFunc
#又一个装饰器函数,在执行函数后打印一下
def print_something_finnaly(func):
def innerFunc(*args,**kwargs):
try:
return func(*args,**kwargs)
finally:
print_now(func,*args,**kwargs)
return func(*args,**kwargs)
return innerFunc
try:
return {"before":print_something_first , "after":print_something_finnaly}[when]
except:
raise ValueError("参数:'before'或者'after'")
@print_factory("after")
def echo_func(name):
print("echo_func :" ,name)
echo_func("我是谁?")