目录:https://www.jianshu.com/p/863c446364a8
装饰器
我们谈装饰器之前先来说说开放封闭原则。
开放封闭原则:
1.开放---对代码的拓展开放 2.封闭---对源码的修改封闭
接着我们再来谈装饰器,什么是装饰器?
装饰器是在不改变原函数的代码以及调用方式的前提下,为其增加新的功能。
装饰器完全遵守开放封闭原则。
装饰器同样拥有三前提:作用域、高阶函数、闭包。学会这三个东西再来学装饰器将是水到渠成。
让我们来做一个银行存款的例子。
def a():
print("-----存款中-----")
a()
我们现在有一个需求,给他增加一个密码验证的过程。
def a():
print("-----存款中-----")
def b():
print("-----密码验证中-----")
b()
a()
或者:
def a():
b()
print("-----存款中-----")
def b():
print("-----密码验证中-----")
a()
这显然违背了开放封闭原则,而且造成了代码冗余。
现在我们对它进行第一次优化:
这一次优化是将存款函数完全放到密码验证函数的保护之中,所以密码验证函数要接受存款函数。
即 我们这些优化的思路是:使用高阶函数来封装a函数,以来避免对a函数的修改。
def a():
print("-----存款中-----")
def b(pwd): # pwd = a
print("-----密码验证中-----")
pwd() #a()
b(a)
这种写法只是没有改变原函数代码,并且增加了新功能。但是他改变了函数的调用方式,这就不符合我们的开放封闭原则。
我们下面进行第二次优化:
这次我们使用闭包来对我们的函数进行优化,这样我们就可以遵循开放封闭原则的前提下,完成功能的增加。这就是一个初级装饰器。
def a():
print("-----存款中-----")def b(pwd): #pwd=a
def inner():
print("-----密码验证中-----")
pwd() # pwd()=a()
return inner #返回inner函数
a=b(a) #inner函数返回到这里,即a=inner
a() #a()=inner(),这里是对inner的调用
接着我们来细致剖析一下初级装饰器的运行过程。
1.第一次读取:读取a函数的整体。
2.第二次读取 :读取b函数的整体
3.接着开始执行 a = b(a) 将 a函数 带入到 b函数 内,即 def b(a) 此时 在b函数内 pwd=a
4.因为 b函数 接受到了参数,系统开始执行 b函数 :b函数内只做了两件事情 定义了一个
inner函数,将inner函数返回。
5. a=b(a)=inner 因为 b(a) 返回了 inner,所以 现在在函数外 a=inner
6.接着执行了 a() 调用了 a 函数 此时 因为 a=inner,所以a()=inner()
7.开始执行 inner(): inner()函数干了两件事情 1.print("-----密码验证中-----") 2.pwd() 因为
pwd=a 所以 pwd() = a()
8.开始调用最开始的a函数, 执行 print("-----存款中-----")
9.执行完成。
其实初级装饰器还可以在优化一下:
将初级装饰器的调用语句换成内置调用方式(语法糖)。
def b(pwd): #这时将a传递给b函数中的pwd 即pwd=a
def inner():
print("-----密码验证中-----")
pwd()
return inner #返回inner函数
@b #这种写法就是语法糖,他就相当于是a=b(a) #inner函数返回给了a
def a ():
print("-----存款中-----")
a() #a函数的调用相当于inner函数的调用
接着我们来细致剖析一下语法糖装饰器的运行过程。
1.第一次读取 :读取b函数的整体。
2.第二次读取 : 因为读取到了 @b, 所以系统接下来的两行看成整体,即:a = b(a) def a():
3.系统开始执行 a = b(a) 将 a函数 带入到 b函数 内,即: def b(a) 此时 在b函数内 pwd=a
4.因为 b函数 接受到了参数,系统开始执行 b函数 : b函数内只做了两件事情 定义了一个 inner函
数,将inner函数返回。
5. a=b(a)=inner 因为 b(a) 返回了 inner,所以 现在在函数外 a=inner
6.最后执行了 a() 调用了 a函数 此时 因为 a=inner a()=inner()
7.开始执行 inner(): inner()函数干了两件事情 1.print("-----密码验证中-----") 2.pwd() 因为
pwd=a 所以 pwd() = a()
8.开始调用最开始的a函数, 执行 print("-----存款中-----")
9.执行完成。
装饰器的返回值
现在我们又有了新的需求,我们想要获取存款的过程,而不是将存款过程直接打印出来,这就需要我们的存款函数a有返回值,现在修改如下:
def b(pwd):
def inner():
print("-----密码验证中-----")
r=pwd() #这里本来是直接调用a函数,现在将他修改为接收a函数的返回值
return r #这里将函数a的返回值返回给最后一句a的调用
return inner
@b
def a ():
return "-----存款中,存款金额为200-----" #a函数的返回值
print(a()) #这里是a的调用,同时是a函数返回值的最后一站
带参装饰器
上面是我们普通装饰器,不能够传递参数,灵活性较差,所以就有了我们的带参装饰器,让我们在上面的例子补充一个新功能可以显示我们的存款金额。
def b(pwd): #这时将a传递给b函数中的pwd 即pwd=a(money)
def inner(*args,**kwargs): #让inner使用不定长参数来接收a的参数 *args=(500)
print("-----密码验证中-----")
pwd(*args,**kwargs) #在这里将500赋给pwd,相当于是a(500)
return inner #返回inner函数
@b #这种写法就是语法糖,他就相当于是a=b(a) #inner函数返回给了a
def a (money): #此时money=500
print("-----存款中,存款金额为{}-----".format(money))
a(500) #a函数的调用相当于inner函数的调用
运行结果为:
-----密码验证中-----
-----存款中,存款金额为500-----
带参语法糖
现在我们再给他美化一下,在密码验证中之后输出一排*,同时我们又要遵守我们的开放封闭原则,所以要使用带参语法糖,修改如下:
def c(char): #这时将*传递给c函数的char即char=*
def b(pwd): #这时将a传递给b函数中的pwd 即pwd=a
def inner():
print("-----密码验证中-----")
print(char*15) #char = “*” 输出 ***************
pwd() #pwd = a 这里调用了a函数 输出 -----存款中-----
return inner #将inner函数返回给a=b(a)
return b #将b函数返回给a=c(*)
@c("*") #在这里相当于执行了两条语句 a=c(*) a=b(a) 这里a接受到了 两个返回值,后一个返回值 innner 覆盖前一个返回值 b 即 a=inner
def a ():
print("-----存款中-----")
a() #a函数的调用相当于inner函数的调用
运行结果为:
-----密码验证中-----
***************
-----存款中-----
标准版的装饰器
def wrapper(f):
def inner(*args,**kwargs):
f(*args,**kwargs)
return inner
设计一个装饰器,显示函数运行的时间。 输出格式为(函数名,时间)
import time
def timmer(f):
def inner ():
start_time=time.time()
f()
end_time=time.time()
print(f,end_time-start_time)
return inner
@timmer fun=timmer(fun)
def fun():
time.sleep(2)
print("Welcome to China")
fun()