Python基础(装饰器)


代码的编写和软件的开发,都应该遵循开放封闭原则。
开放封闭原则(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')

评论关闭