1.什么是装饰器
装饰器(Decorators)是 Python 的一个重要部分。装饰器本质是函数(具有特定功能的函数),装饰器的功能就是装饰其他函数,也就是为其他函数添加一些附属功能。
2.装饰器原则
a.不能修改被装饰函数的源代码。
b.不能修改被装饰函数的调用方式。(装饰器对于被装饰函数来说是透明的)
3.预备知识:
a.函数即“变量”。
b.高阶函数
(1)把一个函数名当做实参传递给另一个函数
(2)返回值中包含函数名。
c.嵌套函数
装饰器=高阶函数+嵌套函数
4.实例
假设我们现在有两个函数。
def a():
print("aaa")
def b():
print("bbb")
a()
b()
输出结果分别是aaa和bbb
如果我们要给两个函数添加打印日志的功能该怎么做呢?可以是下面这种做法。
def a():
print("aaa")
print("打印日志")
def b():
print("bbb")
print("打印日志")
执行结果为:
aaa 打印日志 bbb 打印日志
但是在实际的生产环境中,a和b函数正在运行一些功能,那我们如何通过不修改其源代码的情况下给它增加打印日志的功能呢?
我们可以通过高阶函数来对其进行装饰。
def decorators(func):
func()
print("打印日志")
return func
def a():
print("aaa")
def b():
print("bbb")
a = decorators(a)
b = decorators(b)
a()
b()
如上,我们可以通过高阶函数来对其进行装饰,但是有点小问题,它的运行结果并不是我们想要的。其运行结果如下;
aaa 打印日志 bbb 打印日志 aaa bbb
因为函数装饰器由高阶函数和嵌套函数组成,所以我们可以对如上代码进行一定的修改。
def decorators(func):
def decorators_():
func()
print("打印日志")
return decorators_
def a():
print("aaa")
def b():
print("bbb")
a = decorators(a)
b = decorators(b)
a()
b()
如上我们在decorators函数中嵌套了一个函数,嵌套的函数来执行装饰效果。执行结果如下:
aaa 打印日志 bbb 打印日志
如上代码实现了简单的装饰器的效果。但是在调用执行的时候,需要先调用装饰器,然后将装饰器执行的结果返回来,再调用执行。这样显得很是麻烦。
python中提供了“@语法糖”,可以让我们省略装饰器返回的执行结果的重新赋值。也就是可以省略如上代码中的a = decorators(a)和b = decorators(b)。代码如下
def decorators(func):
def decorators_():
func()
print("打印日志")
return decorators_
@decorators
def a():
print("aaa")
@decorators
def b():
print("bbb")
a()
b()
上述代码的执行结果如下:
aaa 打印日志 bbb 打印日志
但是有时候我们的函数是有参数的,因为@decorators,本质上等于a = decorators_(a),所以我们在传参数的时候,只需要将参数传递给decorators_这个函数就行。修改后代码如下:
def decorators(func):
def decorators_(*args,**kwargs):
func(*args,**kwargs)
print("打印日志")
return decorators_
@decorators
def a(parameter_1,parameter_2):
print("aaa",parameter_1,parameter_2)
@decorators
def b(parameter_1,parameter_2):
print("bbb",parameter_1,parameter_2)
a("aaa",3333)
b("bbb",4444)
如上,运行结果为:
aaa aaa 3333
打印日志
bbb bbb 4444
打印日志
如上,我们便完成了基本的装饰器。
装饰器的工作原理:以上述代码为例,首先查看我们的decorators函数,它接受一个参数,也就是函数名,在decorators内部又嵌套了一个函数decorators_,该函数接受由被修饰函数传进来的参数,然后再调用被修饰函数,然后再执行打印日志(也就是修饰器的功能),decorators函数的返回值其实就是decorators_。
再来看我们的被修饰函数,以a()为例,当python解释器遇到@decorators时,就会去调用执行decorators函数,@decorators相当于前面说的a = decorators_(a)。然后经过decorators_的修饰后,decorators的返回值赋值给了a,此时a是已经被修饰过了的,此时的a指向了decorators的内嵌函数decorators_的地址,然后我们再执行a()就是相当于调用的是decorators.decorators_,执行完成后,便完成了对a()函数的修饰。
类装饰器
class Test(object):
def __init__(self, func):
print('test init')
print('func name is %s ' % func.__name__)
self.__func = func
def __call__(self, *args, **kwargs):
print('打印日志')
self.__func()
@Test
def test():
print("aaa")
test()
执行结果如下:
test init
func name is test
打印日志
aaa