装饰器
一:开放封闭原则
软件一旦上线后,就必须遵循开放封闭原则,具体内容是:
对修改源代码封闭,对功能的扩展是开放的。
软件上线后不允许修改源代码,那么如何实现对功能的扩展了?我们需要一个解决方案,在不修改源代码及调用方式的前提下,对软件功能进行扩展。
将要求和实现目标做一个总结:
要求一:不修改源代码
要求二:不修改调用方式
实现目标:给软件扩展新功能
二:.装饰器是什么?
从字面上的意思来看,器是工具的意思,装饰是为被装饰对象添加新功能。就如同我们在生活中一样,我们给自行车装饰上一个响铃。在这个过程中,没有改变自行车的结构(源代码),也没有让自行车的使用方式发生改变(还是骑着走),但是自行车被添加了一个新功能:响铃。于是在上述两个前提下,实现了扩展新功能的目的。
将内容提炼一下可以知道:
装饰器即在不修改装饰对象源代码和调用方式的前提下,为被装饰对象添加功能。
注意:装饰器和被装饰对象均是可以任意调用的对象。
装饰器的语法:
@deco1 @deco2 @deco3 def foo(): pass foo=deco1(deco2(deco3(foo)))
三:从闭包函数到无参装饰器
装饰器就是闭包函数的一种应用场景
def foo(): x =10 def bar(): print("welcome to %s page"%x) return bar
上面的函数bar就是一个典型的闭包函数
定义一个无参函数index
impor time def index(): time.sleep(3) print("welcome to index page")
现在我们想要实现一个统计程序运行时间的功能有以下几种实现方法:
功能扩展方案一:修改源代码
import time def index(): start_time = time.time() time.sleep(3) print("welcome to index page") stop_time = time.time() print("run time is %s" % (stop_time - start_time)) index()
功能扩展方案二:不修改源代码,但是代码冗余,可用性不强,仅能装饰index
import time def index(): time.sleep(3) print("welcome to index page") # 装饰index start_time = time.time() index() stop_time = time.time() print("run time is %s" % (stop_time - start_time))
功能扩展方案三:把装饰功能写成函数,但是函数的调用方式改变了:
import time def wrapper(func): start_time = time.time() res = func() stop_time = time.time() print("run time is %s" % (stop_time - start_time)) return res wrapper(index)
上述三种方式中的功能扩展都实现了,但是要么易用性不强,要么就是修改源码或者更改调用方式,在软件上线后,牵一发而动全身,修改源码和调用方式的改变都是不可取的。在这里就是闭包函数出场的时候了,闭包函数的特性是把函数体封装在某一作用域之中,而函数的作用域在函数定义时就已经固定了,利用这些特点可以定义一个通用性的无参装饰器
功能扩展方案四:把装饰功能写成函数,但是只能装饰无参函数:
import time def timer(func): def wrapper(): start_time = time.time() res = func() stop_time = time.time() print("run time is %s" % (stop_time - start_time)) return res return wrapper @timer # index = timer(index) = wrapper def index(): time.sleep(3) print("welcome to index page") index()
这里调用的函数index来自于@timer(#index = timer(index) = wrapper)中,实际上调用的是闭包函数wrapper,闭包函数的参数func在@timer时(timer(index))已经写入func=index写死了,最后的执行结果就是原函数index加上装饰的效果。
四:装饰器的进阶:有参函数装饰器
被装饰的函数需要传入参数,根据无参函数装饰器,被装饰函数的实参需要传到被返回的函数中。
把传入的参数原封不动的传入其他函数,用*args和**kwargs。
把装饰功能写成函数,可以装饰一切函数:
import time def timer(func): def wrapper(*args, **kwargs): # 接受传入的实参 start_time = time.time() res = func(*args, **kwargs) # 原封不动获取传入的参数 stop_time = time.time() print("run time is %s" % (stop_time - start_time)) return res return wrapper @timer # index = timer(index) = wrapper def index(): time.sleep(3) print("welcome to index page") @timer def home(name): time.sleep(3) print("welcome %s page to home" %name) index() print("=======") home("pill")