代码的编写和软件的开发,都应该遵循开放封闭原则。
开放封闭原则(OCP,Open Closed Principle)是所有面向对象原则的核心。其核心思想是:
对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。
对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对类进行任何修改。
而装饰器(就是函数)的作用就是为已经存在的对象添加额外的功能。
此篇文章主要介绍装饰器的五种使用情况。
需求场景:
让一个已经开发完成的功能在执行之前,先执行某些操作。
一、基本用法
原函数:
def func1():
print ‘old func1‘
func1()
执行结果:
old func1
定义装饰器:
def w1(func): #这里参数func的值就是被装饰的函数的函数名
def _w1(): #定义一个新的函数
print ‘Begin new func %s()‘%func.func_name #装饰器中新加入的操作
func() #执行完新加入操作后开始执行原函数
return _w1 #返回被重新装饰过的函数,函数名为_w1
使用装饰器装饰原函数:
@w1 #装饰器用法,@+装饰器函数名,放在需要被装饰的函数上面
def func1():
print ‘old func1‘
func1()
执行结果:
Begin new func func1()
old func1
二、动态参数,可以装饰带有N个(N>=0)参数的函数
def w2(func):
def _w2(*args,**kwargs): #可接受任意参数的函数
print ‘Begin new func %s()‘%func.func_name
func(*args,**kwargs)
return _w2
@w2
def func1(): #不带参数的函数
print ‘old func1‘
@w2
def func2(*args,**kwargs): #带任意参数的参数
print ‘old func2,arg is %s‘%args
func1() #执行不带参数的函数
func2(‘test‘) #执行带参数的函数,并赋值一个test参数
执行结果:
Begin new func func1()
old func1
Begin new func func2()
old func2,arg is test
三、被装饰的函数带有return值
错误写法:
def w3(func):
def _w3(*args,**kwargs):
print ‘Begin new func %s()‘%func.func_name
func(*args,**kwargs)
return _w3
@w3
def func3(*args,**kwargs):
print ‘old func3,arg is %s‘%args
return ‘done‘ #定义函数的返回值为done
re = func3(‘test‘) #获取函数的返回值
print re #打印函数的返回值
执行结果:
Begin new func func3()
old func3,arg is test
None #结果函数的返回值为None
正确写法:
def w3(func):
def _w3(*args,**kwargs):
print ‘Begin new func %s()‘%func.func_name
return func(*args,**kwargs)
return _w3
@w3
def func3(*args,**kwargs):
print ‘old func3,arg is %s‘%args
return ‘done‘
re = func3(‘test‘)
print re
执行结果:
Begin new func func3()
old func3,arg is test
done #得到正确的返回值,请自行查看两种写法的差别。
四、多个装饰器装饰同一个函数(多个装饰器的情况下,从里往外(下往上)装饰,装饰完成后,从外往里(上往下)执行)
应用场景:同一个函数希望扩展两个完全不一样的功能,比如一个验证功能,一个记录日志的功能。
用一个例子进行说明:
def first(func): #装饰器1
print ‘%s() was post to first()‘%func.func_name
def _first(*args,**kw):
print ‘Call the function %s() in _first().‘%func.func_name
return func(*args,**kw)
return _first
def second(func): #装饰器2
print ‘%s() was post to second()‘%func.func_name
def _second(*args,**kw):
print ‘Call the function %s() in _second().‘%func.func_name
return func(*args,**kw)
return _second
@first #装饰器1
@second #装饰器2
def test(*args,**kwargs):
print ‘test() args -->‘,args
test(‘123‘)
执行结果:
test() was post to second()
_second() was post to first()
Call the function _second() in _first().
Call the function test() in _second().
test() args --> (‘123‘,)
逐条解释:
test() was post to second() #由于装饰器second在test函数上面,所以会先用装饰器second装饰test,这时候装饰器second的参数是test,执行后的返回值是_second函数,里面的func变量值是test函数
_second() was post to first() #由于_second函数上还有一个装饰器first,所以会用装饰器first装饰_second,这时候装饰器first的参数是_second, 执行后的返回值是_first函数,里面的func变量值是_second函数
Call the function _second() in _first(). #所有装饰器装饰完成,开始执行,这里会先执行_first函数,等于在里面执行_second()
Call the function test() in _second(). #开始执行_second函数,等于在里面执行test()
test() args --> (‘123‘,) #test函数执行的结果
五、多层装饰器,(带参数的装饰器、不要和第二种情况搞混)
应用场景:当希望使用装饰器装饰函数时,装饰器内部调用的方法是可定义的,比如装饰A函数时,内部调用AA方法,装饰B函数时,内部调用BB方法。
用一个例子进行说明:
def Before(request,kargs):
print ‘before -->‘,request
def After(request,kargs):
print ‘after <--‘,kargs
def Filter(before_func,after_func): #第一层装饰器
def outer(main_func): #第二层装饰器
def wrapper(request,kargs): #第二层装饰器定义的函数
before_result = before_func(request,kargs)
if(before_result != None):
return before_result;
main_result = main_func(request,kargs)
if(main_result != None):
return main_result;
after_result = after_func(request,kargs)
if(after_result != None):
return after_result;
return wrapper #第二层装饰器返回的函数
return outer #第一层装饰器返回的函数,也就是另一个装饰器
@Filter(Before, After)
def Index(request,kargs):
print ‘index‘
Index(‘begin‘,‘end‘)
执行结果:
before --> begin
index
after <-- end
执行过程:
1、解释器读取到装饰器@Filter(Before, After),开始执行Filter(Before, After)函数
2、进入Filter函数执行,并传入参数‘Before, After‘,此时Filter函数中参数before_func=Before,参数after_func=After。
继续往下执行,函数中定义了一个outer函数,由于未调用该函数,所以不执行该函数,继续往下执行
3、Filter函数return了outer函数作为本次执行的返回值
4、根据Filter函数执行的返回值创建了新的装饰器@outer
5、开始执行outer函数,此时其参数main_func的值为原函数Index,也就是main_func=Index
6、outer函数中定义了另一个函数wrapper,其参数的值是原函数Index传入的参数值,由于未调用该函数,所以不执行该函数,继续往下执行
7、outer函数return了wrapper函数作为本次执行的返回值
8、原函数Index被重新赋值等于wrapper函数,也就是Index=wrapper
9、开始执行新的Index函数(即wrapper函数)也就是wrapper(‘begin‘,‘end‘)
10、执行时会先执行before_func(request,kargs),也就是Before(‘begin‘,‘end‘)
11、再执行main_func(request,kargs),也就是Index(‘begin‘,‘end‘)
12、最后执行after_func(request,kargs),也就是After(‘begin‘,‘end‘)
执行完成。