内裤可以用来遮羞,但到了冬天它没办法帮我们防风御寒,聪明的人们发明了长裤,穿上了长裤人们再也不冷了。装饰器就像这里说的长裤,在不影响内裤作用的前提下,给我们的身子提供了保暖的功效。
在python中,装饰器的本质就是一个函数,它可以让其他函数在不需要做任何变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括地讲,装饰器的作用就是为已经存在的对象添加额外的功能。
def outer(func): # 定义装饰器函数,有一个参数func,用于导入被装饰的函数 def inner(): # 定义装饰好的,添加了新功能的函数 print("这里写函数执行前要添加的功能") r = func() # 运行被装饰函数,并用r接收被装饰函数运行后的返回值 print("这里写函数执行后要添加的功能") return r # 将被装饰函数的返回值(即r)写入新函数的返回值 return inner # 将新函数的函数名当做装饰器函数的返回值 """ 接下来使用装饰器,用关键字@+装饰器函数名,@语法有两个作用: 1.执行装饰器函数outer,并将下一句的函数名(即foo)当做参数; 2.将装饰器函数outer的返回值重新赋值给下一句的函数 """ @outer def foo(): print("I'm a foo!") foo() # 此时的foo函数内部已经被装饰成了新函数,执行即可. 输出: >>>这里写函数执行前要添加的功能 >>>I'm a foo! >>>这里写函数执行后要添加的功能
可以看到,在没有修改原函数foo的前提下,我们在运行foo()时为其添加了我们想要的功能.
例1:
def outer(func): return "123" @outer def f1(): return True print(f1) 输出: >>>123
解释器执行到@outer后,首先将f1当做参数传入outer函数内,然后将outer的返回值重新赋值给f1,即f1指向返回值的内存地址,因此这时的f1不是函数而是一个指向字符串的变量名.
思考一个问题,既然f1可以指向字符串,那么是不是也可以让它指向一个函数呢?换句话说,把例1中的”123”改为另一个函数,那么f1运行的就是另一个函数.见例2:
def outer(func): def f2(): pass return f2 @outer def f1(): return True
这样一来,f1就指向了f2函数,运行f1()时,就相当于运行了f2().
如此一来有一个问题,f1已经被f2取代了,这不符合我们给原函数添加功能的目的,还需要做进一步修改.
例3:
def outer(func): def f2(): r = func() return r return f2 @outer def f1(): return True
我们在f2函数内部执行f1,并将f1的返回值当做自己的返回值返回,这样一来,运行f1就等于运行f2,而f2内部包含原f1函数.一个装饰器的模型就出现了.
def outer(func): def inner(v1,v2): print("新增功能") r = func(v1,v2) return r return inner @outer def f1(x,y): print("这是一个带参数的函数") return x+y print(f1(1,2)) 输出: >>>新增功能 >>>这是一个带参数的函数 >>>3
如果原函数有参数,那么定义inner函数时加上对应参数即可.
def outer(func): def inner(*args,**kwargs): print("新增功能") r = func(*args,**kwargs) return r return inner @outer def f1(x,y,*args,**kwargs): print("这是一个带有N个参数的函数") return [x,y,args,kwargs] r = f1(1,2,"aaa",k="bbb") print(r) 输出: >>>新增功能 >>>这是一个带有N个参数的函数 >>>[1, 2, ('aaa',), {'k': 'bbb'}]
依葫芦画瓢,不再赘述,此例中的装饰器可以装饰含有N个参数的函数.
def outer0(func): def inner(*args,**kwargs): print("新增功能1") r = func(*args,**kwargs) return r return inner def outer1(func): def inner(*args,**kwargs): print("新增功能2") r = func(*args,**kwargs) return r return inner @outer0 @outer1 def f1(x,y): print("111") return x+y ret = f1(1,2) print(ret) 输出: >>>新增功能1 >>>新增功能2 >>>111 >>>3
这里@outer1和@outer2,新增了两个功能.
可见,一个函数可以应用多个装饰器,按顺序@即可