欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Python中的多重装饰器

程序员文章站 2022-06-14 16:47:12
多重装饰器,即多个装饰器修饰同一个对象【实际上并非完全如此,且看下文详解】 1.装饰器无参数: 复制代码 代码如下: >>> def first(f...

多重装饰器,即多个装饰器修饰同一个对象【实际上并非完全如此,且看下文详解】

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的责任。

像上面这种,对代码有两种互不相干的检验时,就可以使用多重装饰器。