Python装饰器
2017-09-05 本文已影响0人
ericsaid
Python装饰器
- 装饰器的本质是什么?
装饰器等价于高阶函数,形如myfunc=decorator(myfunc),接受函数或类作为输入,以函数或类作为输出。在@语法糖中,并没有要求输出为一个函数或者类。即
def decorator(func):
return None
@decorator
def foo():
pass
也可以作为@decorator语法装饰函数或类。
所以,可以将装饰器当成是一个接受函数或类为输入的函数即可。
- 装饰器的使用场景
- 插入日志
- 性能测试
- 事务处理
- 权限认证
- 修改传入参数/参数校验
- 结果预处理
- 截断函数的执行
- …
- 编写不带装饰器参数的装饰器
def decorator(func):
# do something when decorator called
def _deco(*args, **kw):
# do something before func called
ret = func(*args, **kw)
# do something after func called
return ret
return _deco
@decorator
def myfunc(*args, **kw):
print "myfunc called"
return True
内部函数“_deco”和myfunc的参数结构需要一致,当然不是要求形式上的一致。如参数为类的装饰器,如下
def singleton(cls):
# do something when decorator called
instances = {}
def _getinstance(*args, **kw):
if cls not in instances:
instances[cls] = cls(*args, **kw)
return instances[cls]
return _getinstance
@singleton
class Myclass(object):
def __init__(self, *args, **kw):
pass
以上示例为构建单例模式的装饰器版本。
- 编写带装饰器参数的装饰器
带参数的装饰器,等价于形如myfunc=decorator(a, b)(myfunc)的函数调用。decorator(a, b)返回一个不带装饰器参数的装饰器,即过程为:decorator1 = decorator(a, b) ;myfunc = decorator1(myfunc)。形式上多了一层函数,不过也可以使用柯里化的方式构建decorator1。如下例:
from functools import partial
def decorator(a, func):
# do something when decorator called
def _deco(*args, **kw):
# do something before func called
ret = func(*args, **kw)
# do something after func called
return ret
return _deco
# or
def decorator_out(*args, **kwargs):
# do something when decorator_out called with parameters
def _outer(func):
# do something when decorator_out called
def _deco(*arg, **kw):
# do something before func called
# of course `*args` or `**kwargs`
ret = func(*arg, **kw)
# do something after func called
return _deco
return _outer
@partial(decorator, "param1")
def myfunc():
print "myfunc"
@decorator_out("params1")
def myfunc1():
print "myfunc1"
- functools.wraps
因为Python中__name__
或__doc__
等metadata是在函数定义中生成的,而无法在执行过程中生成,所以myfunc=decorator(myfunc)执行之后,myfunc的metadata会发生变化,如myfunc.__name__
,解决方案是装饰器中,加上functools.wraps。如上例:
from functools import wraps
def decorator_out(*args, **kwargs):
# do something when decorator_out called with parameters
def _outer(func):
# do something when decorator_out called
@wraps(func)
def _deco(*arg, **kw):
# do something before func called
# of course `*args` or `**kwargs`
ret = func(*arg, **kw)
# do something after func called
return ret
return _deco
return _outer