Python中的多重装饰器
多重装饰器,即多个装饰器修饰同一个对象【实际上并非完全如此,且看下文详解】
1.装饰器无参数:
>>> def first(func):
print '%s() was post to first()'%func.func_name
def _first(*args,**kw):
print 'call the function %s() in _first().'%func.func_name
return func(*args,**kw)
return _first
>>> def second(func):
print '%s() was post to second()'%func.func_name
def _second(*args,**kw):
print 'call the function %s() in _second().'%func.func_name
return func(*args,**kw)
return _second
>>> @first
@second
def test():return 'hello world'
test() was post to second()
_second() was post to first()
>>> test()
call the function _second() in _first().
call the function test() in _second().
'hello world'
>>>
实际上它是相当于下面的代码:
>>> def test():
return 'hello world'
>>> test=second(test)
test() was post to second()
>>> test
<function _second at 0x000000000316d3c8>
>>> test=first(test)
_second() was post to first()
>>> test
<function _first at 0x000000000316d358>
>>> test()
call the function _second() in _first().
call the function test() in _second().
'hello world'
>>>
2.装饰器有参数:
>>> def first(printresult=false):
def _first(func):
print '%s() was post to _first()'%func.func_name
def __first(*args,**kw):
print 'call the function %s() in __first().'%\
func.func_name
if printresult:
print func(*args,**kw),'#print in __first().'
else:
return func(*args,**kw)
return __first
return _first
>>> def second(printresult=false):
def _second(func):
print '%s() was post to _second()'%func.func_name
def __second(*args,**kw):
print 'call the function %s() in __second().'%\
func.func_name
if printresult:
print func(*args,**kw),'#print in __second().'
else:
return func(*args,**kw)
return __second
return _second
>>> @first(true)
@second(true)
def test():
return 'hello world'
test() was post to _second()
__second() was post to _first()
>>> test()
call the function __second() in __first().
call the function test() in __second().
hello world #print in __second().
none #print in __first().
>>>
如上,第35行输出后调用__second(),而__second()中又调用了test()并print test(),而后返回__first()中继续执行print,而这个print语句print的内容是__second()返回的none
它等同于:
>>> def test():
return 'hello world'
>>> test=second(true)(test)
test() was post to _second()
>>>
>>> test
<function __second at 0x000000000316d2e8>
>>> test=first(true)(test)
__second() was post to _first()
>>> test
<function __first at 0x0000000003344c18>
>>>
3.多重装饰器的应用:
比如你是项目经理,你要求每一个代码块都必须有参数检查argstype和责任检查responsibilityregister,这样就需要两个装饰器对此代码块进行监督。
#coding=utf-8
import os,sys,re
from collections import ordereddict
def argstype(*argtypes,**kwtypes):
u'''argstype(*argtypes,**kwtypes)
options=[('opt_usetypeofdefaultvalue',false)]
以下为本函数相关的开关,并非类型检验相关的关键字参数,所有options:
opt_usetypeofdefaultvalue=>bool:false,为true时,将对没有指定类型的带默
认值的参数使用其默认值的类型
'''
def _argstype(func):
#确定所有的parameter name
argnames=func.func_code.co_varnames[:func.func_code.co_argcount]
#确定所有的default parameter
defaults=func.func_defaults
if defaults:
defaults=dict(zip(argnames[-len(defaults):],defaults))
else:defaults=none
#将“参数类型关键字参数”中的所有“options关键字参数”提出
options=dict()
for option,default in [('opt_usetypeofdefaultvalue',false)]:
options[option]=kwtypes.pop(option,default)
#argtypes和kwtypes的总长度应该与argnames一致
if len(argtypes)+len(kwtypes)>len(argnames):
raise exception('too much types to check %s().'%func.func_name)
#所有kwtypes中的键不能覆盖在argtypes中已经占用的names
if not set(argnames[len(argtypes):]).issuperset(
set(kwtypes.keys())):
raise exception('there is some key in kwtypes '+
'which is not in argnames.')
#确定所有的参数应该有的types
types=ordereddict()
for name in argnames:types[name]=none
if len(argtypes):
for i in range(len(argtypes)):
name=argnames[i]
types[name]=argtypes[i]
else:
for name,t in kwtypes.items():
types[name]=t
if len(kwtypes):
for name,t in kwtypes.items():
types[name]=t
#关于default parameter的type
if options['opt_usetypeofdefaultvalue']:
for k,v in defaults.items():
#如果default parameter的type没有另外指定,那么就使用
#default parameter的default value的type
if types[k]==none:
types[k]=type(v)
def __argstype(*args,**kw):
#order the args
args=ordereddict()
#init keys
for name in argnames:args[name]=none
#init default values
if defaults is not none:
for k,v in defaults.items():
args[k]=v
#fill in all args
for i in range(len(args)):
args[argnames[i]]=args[i]
#fill in all keyword args
for k,v in kw.items():
args[k]=v
#check if there is some none in the values
if defaults==none:
for k in args:
if args[k]==none:
if defaults==none:
raise exception(('%s() needs %r parameter, '+
'which was not given')%(func.func_name,k))
else:
if not defaults.has_key(k):
raise exception(('parameter %r of %s() is'+
' not a default parameter')%\
(k,func.func_name))
#check all types
for k in args:
if not isinstance(args[k],types[k]):
raise typeerror(('parameter %r of %s() must be '+
'a %r object, but you post: %r')%\
(k,func.func_name,types[k],args[k]))
return func(*args,**kw)
return __argstype
return _argstype
def responsibilityregister(author):
def _responsibilityregister(func):
def __responsibilityregister(*args,**kw):
try:
return func(*args,**kw)
except exception as e:
print ("something is wrong, it's %s's responsibility."%\
author).center(80,'*')
raise e
return __responsibilityregister
return _responsibilityregister
@responsibilityregister('kate')
@argstype(str,int)
def left(str,len=1):
return str[:len]
print 'good calling:'
print left('hello world',8)
print 'bad calling:'
print left(3,7)
这里没有文档,所以调用者不知道,使用了错误的调用,导致出错,这是kate的责任。
像上面这种,对代码有两种互不相干的检验时,就可以使用多重装饰器。
上一篇: 如何使用Bootstrap 按钮实例详解