装饰器的引入
- 我们可以直接通过修改函数中的代码来完成需求,但会产生以下一些问题。
- 如果修改的函数多,修改起来会比较麻烦。
- 不方便后期的维护。
- 这样做会违反OCP原则(ocp:开闭原则,程序的设计,要求开发对程序的扩展,要关闭对程序的修改)
- 装饰器的作用:
- 通过装饰器,可以在不修改原来函数的情况下对函数进行扩展。
- 在开发中,我们都是通过装饰器来扩展函数功能的。
一、普通装饰器的创建
先决条件:因为装饰器本质上是一个闭包,所以它也符合闭包的三个特性:
- 函数嵌套
- 内部函数需要用到外部函数的变量或者参数
- 外部函数返回内部函数的函数对象
def fun_out(fn):
print("我是外部函数")
def fun_inner():
print("我是内部函数")
fn()
return fun_inner
以上就是这个高级函数就是一个闭包了,既然有装饰器的使用,那么当然也需要一个被装饰的目标函数了,接下来创建一个被装饰的目标函数,如下:
def fun():
print("我是装饰的第一个目标函数")
现在准备工具已经完成,我们来调用程序看看这个结果。
def fun_out(fn):
print("我是外部函数")
def fun_inner():
print("我是外部函数")
fn()
return fun_inner
def fun():
print("我是装饰的第一个目标函数")
f = fun_out(fun)
f()
#执行结果如下
我是外部函数
我是外部函数
我是装饰的第一个目标函数
那么这就是我们的执行结果了,可以看到我们完成了程序的添加,但是并没有对程序本身功能做更改。但是这么写不是太友好,如果说使用人员使用这个程序,步骤的增多会降低体验度,同时,这样也会把开发人员的目光吸引到如何调用装饰器上,而不是针对目标函数做装饰,所以我们一般是使用语法糖的写法。
def fun_out(fn):
print("我是外部函数")
def fun_inner():
print("我是外部函数")
fn()
return fun_inner
@fun_out
def fun():
print("我是装饰的第一个目标函数")
#f = fun_out(fun)
fun()
输出结果如下:
我是外部函数
我是外部函数
我是装饰的第一个目标函数
这样的使用显然更加的便捷。
通用装饰器的创建
含义:装饰的函数是否携带参数都可以保证程序的正常运行,则称之为通用装饰器。
在上面的例子中我们可以发现,如果在调用的时候时候给他传递参数的时候会怎样呢,答案肯定是会报错的,又或者说在创建装饰器的时候,需要传递参数但调用的时候没有传递参数的话也会报错,这样就显得很不方便。所以我们给内部函数添加不定长参数,不管目标函数是否携带参数都能够使用此装饰器。
def fun_out(fn):
print("我是外部函数")
def fun_inner(*args,**kwargs):
print("我是外部函数")
fn(*args,**kwargs)
return fun_inner
@fun_out
def fun(a,b):
print("我是装饰的第一个目标函数")
print(a + b )
#f = fun_out(fun)
fun(10,100)
输出结果如下:
我是外部函数
我是外部函数
我是装饰的第一个目标函数
110
三、携带参数的装饰器的使用
携带参数的装饰器是一个比较特殊的装饰器了,当我们使用的装饰器是携带了参数的时候,我们必须在装饰器的外层在包裹一层函数才可以。
def loggin(sign):
def fun_out(fun):
def fun_inner(num1,num2):
if sign == "*":
print("正在调用乘法运算")
elif sign == "/":
print("正在调用除法运算")
result = fun(num1,num2)
return result
return fun_inner
return fun_out
@loggin("/")
def func(a,b):
res = a / b
return res
@loggin("*")
def func2(a,b):
res2 = a * b
return res2
res = func(8,2)
print(res)
res2 = func2(8,2)
print(res2)
#输出结果:
正在调用除法运算
4.0
正在调用乘法运算
16
四、多个装饰器的使用
我们都知道使用一个装饰器,我们的目标函数没有问题,哪如果我们使用多个装饰器会是怎样的呢?
import time
def fun_out(fun):
def fun_inner(*args, **kwargs):
begin = time.time()
res = fun(*args, **kwargs)
end = time.time()
print('程序执行时间为%s' % (end - begin))
return res
return fun_inner
def login(fun):
def wrapper(*args, **kwargs):
print('请先登录')
res = fun(*args, **kwargs)
return res
return wrapper
@fun_out
@login
def fun1(a, b):
time.sleep(1)
return a + b
r = fun1(1, 2)
print(r)
运行结果如下:
请先登录
程序执行时间为1.0003249645233154
3
那么大家有没有注意到程序的执行顺序问题,装饰器的执行顺序是从下往上执行的,并不是我们想象中的从上往下执行,这个是多个装饰器同时装饰一个目标函数的特殊性质,哪个装饰器离目标函数近,那么那个装饰器就先执行。