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