三、装饰器
内裤可以用来遮羞,但到了冬天它没办法帮我们防风御寒,聪明的人们发明了长裤,穿上了长裤人们再也不冷了。装饰器就像这里说的长裤,在不影响内裤作用的前提下,给我们的身子提供了保暖的功效。
在python中,装饰器的本质就是一个函数,它可以让其他函数在不需要做任何变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括地讲,装饰器的作用就是为已经存在的对象添加额外的功能。
1 装饰无参数函数
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函数.一个装饰器的模型就出现了.
2 装饰有参数函数
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函数时加上对应参数即可.
3 装饰带有N个参数的函数
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个参数的函数.
4 多个装饰器装饰一个函数
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,新增了两个功能.
可见,一个函数可以应用多个装饰器,按顺序@即可