首页 > 代码库 > python装饰器之自己的理解,欢迎更正

python装饰器之自己的理解,欢迎更正

<style>pre.cjk { font-family: "Nimbus Mono L", monospace } h2.western { font-family: "Liberation Sans", sans-serif; font-size: 16pt } h2.cjk { font-family: "Noto Sans CJK SC Regular"; font-size: 16pt } h2.ctl { font-family: "FreeSans"; font-size: 16pt } p { margin-bottom: 0.1in; line-height: 120% }</style>

python装饰器

装饰模式有很多经典的使用场景,例如插入日志、性能测试、事务处理等等,有了装饰器,就可以提取大量函数中与本身功能无关的类似代码,从而达到代码重用的目的。

闭包

两个函数嵌套,内函数使用外函数的变量,外函数的返回值是内函数的引用。

例子一:

#_*_coding:utf-8_*_
def bibao(number):
    print bibao function start
    def bibao_in(number2):
    #需要用到外函数的变量number
    return number + number2    
    #返回的是内函数的引用
    return bibao_in

#调用函数bibao(100)后,return回bibao_in函数的引用,test1就指向bibao_in
test1 = bibao(100)

#test1()调用的是bibao_in函数
print test1(200)

#又创建了一个指向bibao_in的对象test2
test2 = bibao(500)
print test2(200)
<style>pre.cjk { font-family: "Nimbus Mono L", monospace } p { margin-bottom: 0.1in; line-height: 120% }</style>

 

闭包中传递的参数是函数

例子二:
#_*_coding:utf-8_*_
def test(fn):
    #形参是一个函数
    def test_passwd():
        print testing password
        if True:
        #调用函数fn
            fn()
    return test_passwd

def test3():
    print a function

#1.将test3指向的函数地址作为形参,传入test中(执行后fn指向test3); 
#2.test()函数返回结果是内函数test_passwd指向的函数体地址
#3.赋值语句,test3被重新定义,现在它指向test_passwd
test3 = test(test3)
#调用test3,也就调用了test_passwd,因它已与test_passwd指向同一函数体
test3()

<style>h2.western { font-family: "Liberation Sans", sans-serif; font-size: 16pt }
h2.cjk { font-family: "Noto Sans CJK SC Regular"; font-size: 16pt }
h2.ctl { font-family: "FreeSans"; font-size: 16pt }
p { margin-bottom: 0.1in; line-height: 120% }</style>

装饰器

例子三:效果与例子二是相同的,只不过此处使用装饰器


#_*_coding:utf-8_*_

def test(fn):
    #形参是一个函数
    def test_passwd():
    	print ‘进行身份验证...‘
    	if True:
	    #调用函数fn
    	    fn()
    return test_passwd
‘‘‘使用语法糖进行装饰,作用就是更改test3指向的函数体,这可以帮助我们在不改变函数内部结构的情况下,增加它的功能(也就是对其装饰,比如对其增加一个验证功能,增加收集日志的功能)。@testtest3 = test(test3)等价‘‘‘
@test
def test3():
    print ‘start test3‘
#调用test3,也就调用了test_passwd,因它已与test_passwd指向同一函数体
test3()

结果:
进行身份验证...
start test3

 



<style>h3.western { font-family: "Liberation Sans", sans-serif; font-size: 14pt }
h3.cjk { font-family: "Noto Sans CJK SC Regular"; font-size: 14pt }
h3.ctl { font-family: "FreeSans"; font-size: 14pt }
pre.cjk { font-family: "Nimbus Mono L", monospace }
p { margin-bottom: 0.1in; line-height: 120% }</style>

如果被装饰的函数带参数呢

例子四:

def func(fn):
    print 开始装饰
    #使用可变参数类型,方便对多个不同参数的函数进行装饰
    def func_in(*args,**kwargs):
        print 校验中
        #fn调用了原来的test,所以一定也要有参数
        fn(*args,**kwargs)
    return func_in

#对有参数的函数进行装饰
@func   
def test(a,b,c):
    print a=%d,b=%d,c=%d % (a,b,c)

@func
def test2(a,b,c,d):
    print a=%d,b=%d,c=%d,d=%d % (a,b,c,d)

#如果不带参数,装饰器也可以对其进行装饰
@func
def test3():
    print i have nothing

#调用时带参数,所以test指向的func_in函数也必须有参数
test(1,2,3)
test2(1,2,3,4)
test3()

结果:

#注意程序执行到@func语法糖时就进行装饰,不是函数被调用的时候被装饰的
开始装饰
开始装饰
开始装饰
校验中
a=1,b=2,c=3
校验中
a=1,b=2,c=3,d=4
校验中
i have nothing

 

<style>pre.cjk { font-family: "Nimbus Mono L", monospace } p { margin-bottom: 0.1in; line-height: 120% }</style>
被装饰的函数有返回值?

例子五:

例子五:
#_*_coding:utf-8_*_
def func(fn):
    print 开始装饰
    #使用可变长参数类型,方便对多个不同参数的函数进行装饰
    def func_in(*args,**kwargs):
        print 校验中
        #fn调用了原来的test,这里用rtn接收函数的返回值,再return rtn,这样就能保证返回原来函数的返回值
        rtn = fn(*args,**kwargs)
        return rtn
    return func_in

#对有参数的函数进行装饰
@func   
def test(a,b,c):
    print a=%d,b=%d,c=%d % (a,b,c)

#对有返回值的函数进行装饰
@func
def test3():
    return i have nothing

#调用时带参数,所以test指向的func_in函数也必须有参数
test(1,2,3)

print test3()

结果:

开始装饰
开始装饰
校验中
a=1,b=2,c=3
校验中
i have nothing

 

<style>pre.cjk { font-family: "Nimbus Mono L", monospace } p { margin-bottom: 0.1in; line-height: 120% }</style>
函数被装饰多次时,装饰的顺序是怎样?
<style>pre.cjk { font-family: "Nimbus Mono L", monospace } p { margin-bottom: 0.1in; line-height: 120% }</style>
例子六:


def makeBold(fn):    # 1
    print start Bold decorate
    def Bold(*args,**kwargs):
        print make words Bold
        rtn = <b>+ fn(*args,**kwargs) + </b>
        return rtn
    return Bold

def makeItalian(fn):    # 2
    print start Italian decorate
    def Italian(*args,**kwargs):
        print make words italian
        rtn = <i> + fn(*args,**kwargs) + </i>
        return rtn
    return Italian

@makeBold    # 3
@makeItalian    # 4
def words():    # 5
    return hello Paris

print words()    # 6

结果:总结来说,python是从内到外进行装饰

start Italian decorate
start Bold decorate
make words Bold
make words italian
<b><i>hello Paris</i></b>

 

<style>td p { margin-bottom: 0in } pre.cjk { font-family: "Nimbus Mono L", monospace } p { margin-bottom: 0.1in; line-height: 120% }</style>
详细解释一下:
<style>td p { margin-bottom: 0in } pre.cjk { font-family: "Nimbus Mono L", monospace } p { margin-bottom: 0.1in; line-height: 120% }</style>

# 1 程序创造一个内存空间, makeBold指向这片地址

# 2 程序创造一个内存空间, makeItalian 指向这片地址
# 3 @makeBold要对下面的函数进行装饰,但发现下面不是函数,而是装饰器,那它就等待下面装饰器装饰完成
<style>pre.cjk { font-family: "Nimbus Mono L", monospace } p { margin-bottom: 0.1in; line-height: 120% }</style>
# 4 @makeItalian进行装饰,words= makeItalian(words):打印‘start Italian decorate‘:
fn就指向了words指向的函数体,words指向了makeItalian函数的返回值,也就是Italian函数体。
# 5 @makeItalian装饰完后,@makeBold对其装饰words= makeBold(words);打印‘start Bold decorate‘
fn就指向了Italianwords就指向makeBold返回值,也就是Bold函数
<style>pre.cjk { font-family: "Nimbus Mono L", monospace } p { margin-bottom: 0.1in; line-height: 120% }</style>
# 6 调用words()后,执行Bold(),打印‘make words Bold‘
执行到fn()时进入Italian(),打印‘make words italian‘
执行到fn()时进入FFFF(),返回hello ParisFFFF完成);
返回<i>hello Paris</i>(Italian完成);
返回<b><i>hello Paris</i></b>(Bold完成)

<style>pre.cjk { font-family: "Nimbus Mono L", monospace } p { margin-bottom: 0.1in; line-height: 120% }</style>
装饰器可以如函数调用一样“堆叠“起来,这里有一个更加普遍的例子,使用了多个装饰器: 
@deco2
@deco1
def func(arg1, arg2, ...): pass

#这和创建一个组合函数是等价的。
def func(arg1, arg2, ...): pass
func = deco2(deco1(func))

#函数组合用数学来定义就像这样: (g · f)(x) = g(f(x))。对于在python中的一致性:
@g
@f
def foo(): pass

#与 foo=g(f(foo))相同

<style>pre.cjk { font-family: "Nimbus Mono L", monospace }
p { margin-bottom: 0.1in; line-height: 120% }</style>
带参数的装饰器

例子六:

#接收一个参数a
def with_arg(a):
    def bibao(fn):
    print start decorate
    #被装饰的函数带参数
        def bibao_in(*arg,**kwarg):
            print i am +a
        #被装饰的参数带返回值
            rtn = fn(*arg,**kwarg)
            return rtn
    #返回bibao_in指向的函数体
        return bibao_in
    #返回bibao指向的函数体
    return bibao

#装饰器带参数时,先将这个参数传入with_arg()函数中,其返回函数bibao,再使用bibao对test1进行装饰
@with_arg(first)
def test1(a,b):
    print i am test1
    return sum is %d%(a+b)

@with_arg(second)
def test2():
    print i am test2

print test1(1,2)
test2()

结果:

start decorate
start decorate
i am first
i am test1
sum is 3
i am second
i am test2

 

类装饰器


未完待续...... 

以下几位装饰器讲解的比较透彻,知乎大神很多,还需继续学习,自己的理解中有很多错误,欢迎指正。

   https://www.zhihu.com/question/26930016 

   http://blog.csdn.net/bluehawksky/article/details/50372244

 

   借鉴:

   http://python.jobbole.com/82344/

 

python装饰器之自己的理解,欢迎更正