装饰器介绍
假设有一个显示鱼数量的函数:
def info(fishnum):
print('The fish number is %s' % fishnum)
#调用
info(2002)
现在需要对此函数添加一个补充信息:说明是在河里的鱼的数量,添加一个 print(‘In river’),但是不能对原来的函数进行任何一点修改 ,如何实现?
有了!!因为python的高级函数可以用函数作为传入参数。可以新建一个高级函数info_before,将info函数作为参数传入info_before(info);
高级函数的函数体内因该是这样的:在执行完需要的操作后,在执行函数参数info:
def info_before(info):
需要的操作
info()
然后再将info_before函数执行结果赋值给info函数就可以了:info=info_before(info),这样就不影响info的调用了
操作如下:
##原来的函数
def info(fishnum):
print('The fish number is %s' % fishnum)
##新定义的高级函数
def info_before(func):
print('In river \r')
func()
##覆盖原来的函数
info=info_before(info)
看起来可行,执行下 info(2002) 试一试:
出错了!!!
原因:info=info_before()即执行了info_before()高级函数,但是高级函数内的函数体中的info()已经执行了info函数,所以,info接收到的是一个print(‘In river \r’)信息和info()函数的运行结果,又因为info()函数没有参数,所以报错。
而我们需要的返回值是一个函数才对!(之后还要调用),修改如下:
##原来的函数
def info(fishnum):
print('The fish number is %s' % fishnum)
##新定义的高级函数
def info_before(func):
print('In river \r')
#修改返回值是函数
retrun info
##覆盖原来的函数
info=info_before(info)
执行下 info(2002) 试一试:
看起来还不错
但是,当不执行 info(2002) 时,发现有个问题:只要进行了 info=info_before() ,就会自动执行 print(‘In river \r’) 这句命令
如果不写这句,那么后面将不能使用info()调用 新定义的高级函数(改为 info_before(info)调用),这样就影响了原来函数的调用
有没有新的办法?
对了,可以在高级函数内在嵌套一个函数,然后将所有操作放在内嵌函数内,这样就不会在执行高级函数时赋给原来函数时 info=info_before() ,自动执行高级函数内的操作了:
##原来的函数
def info(fishnum):
print('The fish number is %s' % fishnum)
##新定义的高级函数
def info_before(func):
#再嵌套一层内部函数,因为原函数info有变量,这里qure函数也应该需要变量,因为之后会将qure函数赋给info来覆盖原函数
def qure(avrg):
print('In river \r')
#此时应该返回的是info()的执行结果,因为之后info=info_before()操作实际是将qure函数赋给了原函数info,之后调用原函数实际上是调用了qure函数,此时应该返回info()的操作结果
func(avrg)#闭包
#返回qure函数给原函数
retrun qure
##覆盖原来的函数,用qure函数覆盖了info函数
info=info_before(info)
执行下 info(2002) 试一试:
完美!!
上述高级函数info_before就是装饰器的定义
装饰器详细介绍
- 在info函数前使用@info_before就表示调用装饰器info_before,相当于:info=info_before(info)表达式。即在执行info前先执行info_before下的内容
- 装饰器小结:
- 装饰器是为了在不影响原函数的代码结构和调用的基础上,给原函数新添加某些操作或者功能的高级函数
- 装饰器输入参数为函数是为了先执行定义的操作,在执行参数函数
- 装饰器内置函数是为了覆盖原函数的时候不执行定义的操作,内置函数参数使用*avrg,**avtg2 动态参数满足原函数所有参数情况,内置函数的函数体内部原函数执行时,参数也为*avrg,**avtg2。内置函数里面执行原函数时需要使用return避免原函数有返回值无法返回
- 装饰返回为函数是为了不影响原函数的调用
- 装饰器最终在python中的表现为:
#装饰器定义 def info_before(func): #qure函数使用动态参数满足原函数任何参数情况 def qure(*avrg,**avtg2): print('In river \r') #注意func参数和qure参数,使用return避免原函数有返回值无法返回 return func(*avrg,**avtg2) #返回qure函数给原函数 return qure #装饰器调用 @info_before def info(fishnum): print('The fish number is %s' % fishnum) info(2002)
含参数的装饰器
- 装饰器info_before新增的操作只是print操作,那么如果是很复杂的代码呢?我们可以将print操作放在一个函数b_fun中,然后在把函数b_fun传入装饰器@info_before(b_fun)
- @info_before(b_fun)含参数的装饰器,只需要将操作方法写在函数中即可,对于装饰器可以使用一个相同的装饰器定义模板,因为不同的装饰器其实就是在原函数之前/之后添加的操作不同而已,对于装饰器本身,都是相同的结构
- 综上,装饰的实际使用应该是这样:
- 有一个装饰模板 @Filter(fun1,fun2) ,它规定了可以传入2个函数参数,第一个函数是执行原函数之前的一些操作方法,第二个函数是执行原函数之后的一些操作方法。那么在使用装饰器时,我们只需要定义fun1和fun2就可以了,至于如何定义装饰器,已经被模板@Filter(fun1, fun2)定义好了。
- 附:Filter模板(已经定义好了,只需要我们定义好函数fun1和fun2 然后传入装饰器就行)
def Filter(fun1,fun2): def outer(main_func): def wrapper(request,kargs): before_result = fun1(request,kargs) if(before_result != None): return before_result; main_result = main_func(request,kargs) if(main_result != None): return main_result; after_result = fun2(request,kargs) if(after_result != None): return after_result; return wrapper return outer
调用装饰器
#装饰器@Filter:在Index函数之前先执行Before函数,在Index函数之后,在执行After函数 @Filter(Before, After) def Index(request,kargs): print 'index'