Python多重装饰器原理与实战:从基础到参数化应用
本文深入解析Python中多重装饰器的执行机制,区分无参与有参两种场景。通过代码示例展示装饰器叠加时的调用顺序与返回值处理,并结合ArgsType与ResponsibilityRegister两个具体案例,演示如何在实际开发中利用多重装饰器实现参数类型检查与错误责任追踪,帮助开发者掌握高阶函数编程技巧。
多重装饰器指的是将多个装饰器应用于同一个目标对象。需要注意的是,这种表述在严格意义上并不完全准确,具体的执行逻辑将在后文详细展开。
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()并打印了test()的返回值,随后返回到_first()中继续执行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)
由于缺乏相关文档,调用者无法得知正确的调用方式,从而导致错误发生。在这种情况下,根据ResponsibilityRegister的定义,这是Kate的责任。
像上述示例中那样,当需要对代码进行两种相互独立的检验时,就可以使用多重装饰器。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









