1、什么是装饰器?
装饰器就是 @函数名 ,加在被装饰函数之前。有时候我们需要对函数功能进行扩展,但是又必须遵守开闭原则(ocp),不能修改函数,这时候就需要使用装饰器。
2、为什么要使用装饰器?
事实上,当我们想要扩展函数功能或者修改函数时,直接修改函数中的某几行代码就可以实现。
但是这是有弊端的。
- 例如我想给一大批的函数加上同一个功能,那么我需要更改的就不是几行代码了,工作非常繁琐。
- 也不方便后期进行维护
- 会违反开闭原则(程序设计要求开发对程序的扩展,要关闭对程序的修改)
综合以上三点,这里我们使用装饰器就再好不过了。
3、装饰器的使用
直接举个例子,给每一个函数在执行前,打印出该函数的名字。
首先看一下没有装饰器的话,我们怎么实现?
def add(*arg):
print(f'{arg}的和是{sum(arg)}')
def print_self(func):
def bedeco(*arg):
print(f'我是{func.__name__}函数^-^')
func(*arg)
return bedeco
f = print_self
f(add)(1,2,3)
'''
out:
我是add函数^-^
(1, 2, 3)的和是6
整个流程是这样的:
首先定义一个函数,把需要被装饰的函数作为参数接受。然后定义一个内部函数,加上我们装饰的内容,同时调用被装饰的函数,实现其功能,还要把这个函数作为返回值return。(注意不同函数的参数不尽相同,这里用不定长参数*args,**kwargs进行统一)
装饰是完成了,但是使用起来过于繁琐。
再来看看装饰器怎么实现:
def print_self(func):
def bedeco(*arg):
print(f'我是{func.__name__}函数^-^')
func(*arg)
return bedeco
@print_self
def add(*arg):
print(f'{arg}的和是{sum(arg)}')
add(1,2,3)
'''
out:
我是add函数^-^
(1, 2, 3)的和是6
'''
同样的结果,只需要在被装饰函数的定义前面加上装饰器即可。
事实上,在调用被装饰函数add之前,add已经变成了print_self(test),也就是装饰器的返回值bedeco。之后在调用add时,add()就相当于bedeco()了,执行的时装饰器的内部函数。
4、总结
- 通过使用装饰器,我们可以在不修改函数的情况下来扩展功能。
- 实际在开发中,我们都是通过使用装饰器来扩展函数功能的。
- 装饰器函数传入的参数必须被装饰函数的对象作为形参,返回值必须是内部函数的象。
5、发散:多个装饰器的装饰顺序
这里我们可以举个例子来试验一下:
def deco1(func):
def excute():
return "<装饰1 "+func()+"装饰1 >"
return excute
def deco2(func):
def excute():
return "<装饰2 "+func()+"装饰2 >"
return excute
@deco1
@deco2
def hello():
return 'hello world'
hello()
'''
out:
'<装饰1 <装饰2 hello world装饰2 >装饰1 >'
'''
这里我认为真相只有一个,那就是离被装饰函数近的装饰器先装饰。
再来验证一下,这次我多做几个装饰器:
def deco1(func):
def excute():
return "<装饰1 "+func()+"装饰1 >"
return excute
def deco2(func):
def excute():
return "<装饰2 "+func()+"装饰2 >"
return excute
def deco3(func):
def excute():
return "<装饰3 "+func()+"装饰3 >"
return excute
def deco4(func):
def excute():
return "<装饰4 "+func()+"装饰4 >"
return excute
def deco5(func):
def excute():
return "<装饰5 "+func()+"装饰5 >"
return excute
@deco1
@deco2
@deco3
@deco4
@deco5
@deco5
def hello():
return 'hello world'
hello()
'''
out:
'<装饰1 <装饰2 <装饰3 <装饰4 <装饰5 <装饰5 hello world装饰5 >装饰5 >装饰4 >装饰3 >装饰2 >装饰1 >'
'''
这次我认为原因显然易见了。