Python装饰器经常被用于切面需求,如插入日志、性能测试、事务处理等。装饰器是解决这类问题优选设计,使用装饰器可以从函数或者类中抽离出大量与函数功能本身无关的高频使用代码并重用它们。简言之装饰器的作用就是为已经存在的对象(函数或者类)添加额外的功能。
1 装饰器示例分析:
1.1 使用装饰器包装函数
(1)引例,不带参数和返回值
为了获取函数f执行时间,且要求不改变函数调用方式(参数列表也不能改变),为实现该目的:
1)我们需要一个封装函数f的函数并添加时间打印语句
2)为确保函数接口不变,我们要返回一个与函数f接口相同的函数
于是有下面代码出现:
import time
#原函数如下
def foo():
print 'test foo()'
# 定义外封装函数,传入待封装函数,并返回另一个附加计时功能的函数
def timef(func):
# 定义一个内嵌的包装函数,给传入的函数加上计时功能,该函数参数接口同传入函数
def wrapper():
start = time.clock()
func()
end =time.clock()
print 'time:', end - start
# 将包装后的函数返回
return wrapper
foo = timef(foo)
foo()
上面的示例使用python语法糖方式@调用封装函数,就是python包装器,如下所示:
import time
def timef(func):
def wrapper():
start = time.clock()
func()
end =time.clock()
print 'time:', end - start
return wrapper
@timef
def foo():
print 'test foo()'
foo()
(2)带参数和返回值情形
def dec(func):
def wrapper(a, b):
print("before myfunc().")
ret = func(a, b)
print("after myfunc(),result: %s" % ret)
return ret
return wrapper
@dec
def add(a, b):
print(" add(%s,%s) is called." % (a, b))
return a + b
add(1, 2)
def dec(func):
def wrapper(*args, **kwargs):
print("before %s." % func.__name__)
ret = func(*args, **kwargs)
print("after %s,result: %s" % (func.__name__, ret))
return ret
return wrapper
@dec
def add1(a, b):
print(" add1(%s,%s) called." % (a, b))
return a+b
@deco
def add2(a, b, c):
print(" add2(%s,%s,%s) called." % (a, b, c))
return a+b+c
add1(1, 2)
add2(1, 2, 3)
(4)装饰器添加说明性参数
def pre_dec(arg):
def dec(func):
def _dec():
print("before %s called [%s]." % (func.__name__, arg))
func()
print(" after %s called [%s]." % (func.__name__, arg))
return _dec
return dec
@pre_dec("mytest")
def func():
print("func() called.")
func()
这样可以在装饰器中使用类
class test:
def __init__(self):
print("test.__init__()not called")
@staticmethod
def cls_f1():
print("test.cls_f1() called")
@staticmethod
def cls_f2():
print("test.cls_f2() called")
def dec(cls):
def _dec(func):
def __dec():
print("before %s called [%s]." % (func.__name__, cls))
cls.cls_f1()
try:
return func()
finally:
cls.cls_f2()
return __dec
return _dec
@dec(test)
def myfunc():
print("myfunc() called.")
myfunc()
(6)多装饰器顺序很重要
def f1(func):
def wrapper():
print("===f1 start===")
func()
print("===f1 end===")
return wrapper
def f2(func):
def wrapper():
print("###f2 start###")
func()
print("###f2 end###")
return wrapper
#交换f1和f2输出不一样,以下调用等价于test=f1(f2(test)),test()
@f1
@f2
def test(para="--:)--"):
print para
test()
file1:mydecorator.py
class test:
def __init__(self):
print("test.__init__()not called")
@staticmethod
def cls_f1():
print("test.cls_f1() called")
@staticmethod
def cls_f2():
print("test.cls_f2() called")
def dec(cls):
def _dec(func):
def __dec(args,
kwargs
): #*args,**kwargs
print("before %s called [%s]." % (func.__name__, cls))
cls.cls_f1()
try:
return func(args,
kwargs
) #*args,**kargs
finally:
cls.cls_f2()
return __dec
return _dec
file2:app.py
from mydecorator import *
class example:
@dec(test)
def myfunc1(self):
print(" myfunc1() called.")
@dec(test)
@dec(test)
def myfunc2(self, x, y):
print(" myfunc2() called.")
return x + y
if __name__=="__main__":
a = example()
print(a.myfunc1())
print(a.myfunc2(1, 2))
1.2 使用装饰器包装类
def dec(aclass):
class new_class:
def __init__(self,age):
print("dec init")
self.wrap = aclass(age)
def display(self):
print("dec display")
self.wrap.display()
return new_class
@dec
class Bird:
def __init__(self,age):
self.age = age
def display(self):
print("age is %s" % self.age)
a = Bird(2)
a.display()
2 内置装饰器
内置的装饰器有三个,分别是staticmethod、classmethod和property,作用分别是把类中定义的方法变成静态方法、类方法和类属性。
class test(object):
def __init__(self, name):
self._name = name
@staticmethod
def test1(name):
return test(name)
@classmethod
def test2(cls):
return test('')
@property
def name(self):
return self._name
上面属性是只读,如要可写需要使用setter
@name.setter def name(self, name): self._name = name
3 functools模块包含的2个装饰器
3.1 wraps(wrapped[, assigned][, updated]):
这是一个很有用的装饰器。函数有几个特殊属性比如函数名,在被装饰后,上例中的函数名会变成包装函数的名字,这个装饰器能将装饰过的函数的特殊属性保留。
import time
import functools
def timef(func):
@functools.wraps(func)
def wrapper():
start = time.clock()
func()
end = time.clock()
print 'time:', end - start
return wrapper
@timef
def foo():
print 'test foo()'
foo()
print foo.__name__ #output foo,如果注释掉@functools.wraps(func)则输出wrapper
3.2 total_ordering(cls):
它的作用是为实现了至少__lt__、__le__、__gt__、__ge__其中一个的类加上其他的比较方法,这是一个类装饰器。