基于开放封闭的原则,对现有的函数想要扩展新功能,则不推荐对源码进行修改,所以装饰器就是为了扩展函数功能所用。
#原函数
def foo(): print("foo...") time.sleep(2)
#现在需要添加的功能是:为函数计时
#如果在原函数进行修改,则为
import time def foo(): start = time.time() print("foo...") time.sleep(2) end = time.time() print("spend %s" %(end - start)) foo() ######## foo... spend 2.0001144409179688
#但是如果有成千上万的函数,逐个修改的工程量极大,所以可以修改为
import time def foo(): print("foo...") time.sleep(2) def show_time(f): start = time.time() f() end = time.time() print("spend %s" %(end - start)) show_time(foo)
#通过另外定义一个函数,将原函数传入,这样对于每个函数则不用重新定义,只需要再传入即可
#但是,现在使用原函数时,不再是使用原函数名来调用,这样在生产环境中会有较大影响
#所以需要使函数调用仍为之前的,但功能是扩展的
def show_time(f): def inner(): start=time.time() f() end=time.time() print("spend %s" %(end-start)) return inner foo=show_time(foo) foo() ######## foo... spend 2.0001142024993896
#通过返回一个扩展后的函数给源函数接收,这样就可调用原有的函数名,还扩展了功能
#在python中,还对装饰器进行了一个优雅的设置
@show_time
#这个为python的一个语法糖,起作用等同于foo=show_time(foo),在原函数前添加此句即实现了功能扩展
#小结:
1.为了实现扩展功能,需要重新定义一个装饰器函数,在装饰器中有一个内部函数inner主要用于功能扩展,且在最后也需要返回inner函数供原函数接收
2.在装饰器中,使用了高阶函数和闭包的概念。其中传入原函数和返回inner函数则是高阶函数的定义,而inner函数块和inner函数中调用传入的原函数方法则是闭包的概念
3.调用装饰器函数,只需要在原函数前加 @装饰器函数名 即可
4.装饰器的实际原理,@语法糖的作用,同下图所示
#以上仅是装饰器的基础使用
#如果原函数需要传入参数,则需要在装饰器的内部函数inner时定义
import time def show_time(f): def inner(*a,**b): start = time.time() f(*a,**b) end = time.time() print("spend %s" %(end - start)) return inner @show_time def add(*x,**y): sums = 0 for i in x: sums += i print(sums) time.sleep(1) add(1,5,6,8) ######## 20 spend 1.0000574588775635
#如果是装饰器需要传入参数,则需要再原装饰器外部再定义一层函数,以便传入参数
import time def logger(flag=''): def show_time(f): def inner(): start = time.time() f() end = time.time() print("spend %s" %(end - start)) if flag=='true': print("logs...") return inner return show_time @logger('true') def foo(): print("foo...") time.sleep(2) foo() ######## foo... spend 2.0001144409179688 logs...
#总结:
1.如果是原函数有参数传入,则在装饰器中的内部函数inner也需要有相同的形参定义
2.如果是装饰器函数需要参数传入,则需要在装饰器函数外部再添加一层函数,以传入参数给装饰器函数使用,且调用时使用的是外层函数而不再是原装饰器函数