Tags : Ajax  apache  awk  besttrace  bootstrap  CDN  Django  git 

常见问题

Python:装饰器

stevezhou      2014.08.26   


装饰器应用场景:不改变原函数的函体及调用形式的情况下,增加函数功能.

个人认为,为满足以上要求,装饰器需要做到以下几点:

1、调用原函数,以保证原函数功能;

2、增加新功能,这部分将在装饰函数中的新函数内完成(用闭包的新函数,因为直接返回原函数,原函数会多执行一次,且装饰函数仅会被执行一次);

3、保证原函数调用形式不变:

a、参数形式不变:这就需要新函数的参数与原函数参数数量一样;

b、函数调用名不变:必须将装饰函数中的新函数返回给与原函数同名的变量;

c、返回值不变:新闭包函数需要返回原函数的返回值(如果原函数有返回值的情况);

 

下面借网上的简单实例来加以说明:

——包装函数直接返回原函数的情况:

def deco(func):
    print 'before func() called.'
    func()
    print 'after func() called.'
    return func
    
def myfunc():
    print 'myfunc() called.'
    
myfunc=deco(myfunc)  
myfunc()

运行结果:

>>>
before func() called.
myfunc() called.
after func() called.

myfunc() called.
>>> myfunc()
myfunc() called.
>>>

 

这种如[2]中提到的,直接返回原函数,并没有得到理想的结果。结果是原函数被多执行了一次,且以后调用myfunc()时,并未调用到包装函数添加的新功能(此处虽然只是两个print语句),而是直接调用了原函数。


为解决上面这个问题,所以需要在包装函数中创建一个闭包函数,包装函数返回此闭包函数,而非直接返回原函数。如下:

——带闭包函数的包装函数

def deco(func):
    def _deco():
      print 'before func() called.'
      func()
      print 'after func() called.'
    return _deco
    
def myfunc():
    print 'myfunc() called.'
    
myfunc=deco(myfunc)  
myfunc()

运行结果:

>>>
before func() called.
myfunc() called.
after func() called.

>>> myfunc()
before func() called.
myfunc() called.
after func() called.

>>>

 

——使用语法糖@来装饰函数

将上面的函数用语法糖@改造一下:

def deco(func):
        def _deco():
            print 'before func() called.'
            func()
            print 'after func() called.'
        return _deco
        
@deco   #相当于 myfunc=deco(myfunc)    
def myfunc():
        print 'myfunc() called.'

myfunc()

运行结果:

>>>
before func() called.
myfunc() called.
after func() called.

>>> myfunc()
before func() called.
myfunc() called.
after func() called.

>>>

 

Ok,与前面是一样的。就这样,一个简单的装饰器就算完成了!

——带参数的装饰器

下面是一个带参数装饰器例子,作用是在原函数基础上对参数的大小作了一次比较:

def deco(func):
    def addxy(x,y):
        if x>y:
            print 'x>y'
        elif x==y:
            print 'x==y'
        else:
            print 'x<y'
            
        rs=func(x,y)        
        return rs
    
    return addxy

@deco
def myfunc(a,b):
    rs=a+b
    print 'a+b is %s'%rs
    return rs

myfunc(1,2)

运行结果:

>>>
x<y
a+b is 3

>>> rs=myfunc(6,4)
x>y
a+b is 10

>>> rs
10

 ——带参数的装饰器
def deco(arg):
    def _deco(func):
        def addxy(x,y):
            print 'before %s called width %s'%(func.__name__,arg)
            rs=func(x,y)
            print 'x+y is %s'%rs
            return rs
        return addxy
    return _deco

@deco('module1') # 相当于 add=deco('module1')(add)
def add(a,b):
    return a+b

add(1,3)

运行结果:

>>>
before add called width module1
x+y is 4

>>> add(5,5)
before add called width module1
x+y is 10

10
>>> add(8,100)
before add called width module1
x+y is 108
108

>>>

 

总的来说,装饰函数的作用就是返回一个新函数,新函数的功能是在原函数执行前或执行后添加额外功能,且新函数的参数与返回值需要与原函数一致。



标签 :  装饰器 上一篇     下一篇