一、什么是装饰器
装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。简单来讲,可以不严谨地把Python的装饰器看做一个包装函数的函数。
比如,有一个函数:
def func():
print 'func() run.' if '__main__' == __name__:
func()
运行后将输出:
func() run
现在需要在函数运行前后打印一条日志, 但是又不希望或者没有权限修改函数内部的结构, 就可以用到装饰器:
def log(function):
def wrapper(*args, **kwargs):
print 'before function [%s()] run.' % function.__name__
rst = function(*args, **kwargs)
print 'after function [%s()] run.' % function.__name__
return rst
return wrapper @log
def func():
print 'func() run.' if '__main__' == __name__:
func()
对于原来的函数"func()"并没有做修改,而是给其使用了装饰器log,运行后的输出为:
before function [func()] run.
func() run.
after function [func()] run.
把"@log"放到func()函数定义的地方,相当于执行了如下语句:
func = log(func)
因为log()返回了一个函数, 所以原本的func指向了log()返回的函数wrapper。wrapper的参数列表为(*args, **kwargs), 所以其可以接受所有的参数调用, 在wrapper中,先打印了一行
'before function [%s()] run.' % function.__name__,然后执行了原来的函数并记录了返回值,在输出
'after function [%s()] run.' % function.__name__ ,后返回了函数的执行结果。
以下为示例:
def log(text=''):
def decorator(function):
@functools.wraps(function)
def wrapper(*args, **kwargs):
print 'before function [%s()] run, text: [%s].' % (function.__name__, text)
rst = function(*args, **kwargs)
print 'after function [%s()] run, text: [%s].' % (function.__name__, text)
return rst
return wrapper
return decorator @log('log text')
def func():
print 'func() run.' if '__main__' == __name__:
func()
输出如下:
before function [func()] run, text: [log text].
func() run.
after function [func()] run, text: [log text].
多层装饰器
多重装饰器的应用:当要求每一个代码块需要两个或多个检查时,这样就需要两个或多个装饰器对此代码块进行监督。
下面给出一个使用装饰器同时对代码进行登录和权限的验证:
USER_INFO={'is_login':True,'user_type':''}
def check_login(func):
def inner(*args,**kwargs):
if USER_INFO.get('is_login',None):
ret=func(*args,**kwargs)
return ret
else:
print('没登录')
return inner
def check_admin(func):
def inner(*args,**kwargs):
if USER_INFO.get('user_type',None)==2:
ret=func(*args,**kwargs)
return ret
else:
print('权限不足')
return inner @check_login
@check_admin
def index():
print('管理员')
index()