Python中的多重装饰器

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

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

相关文章

pycharm 使用心得(一)安装和首次使用

pycharm 使用心得(一)安装和首次使用

首先预览一下 PyCharm 在实际应用中的界面:(更改了PyCharm的默认风格) 安装 首先去下载最新的pycharm 2.7.3,进行安装。可以直接在官网下载。 PyCharm...

Python 3.6 中使用pdfminer解析pdf文件的实现

Python 3.6 中使用pdfminer解析pdf文件的实现

所使用python环境为最新的3.6版本 一、安装pdfminer模块 安装anaconda后,直接可以通过pip安装 pip install pdfminer3k  ...

运行django项目指定IP和端口的方法

运行django项目指定IP和端口的方法

一、django项目启动命令 默认IP和端口 python manage.py runserver 指定端口 python manage.py runserver 192.1...

Python 快速实现CLI 应用程序的脚手架

Python 快速实现CLI 应用程序的脚手架

今天跟大家分享一下如何快速实现一个Python CLI应用程序的脚手架,之所以会做这个是因为当时需要做一个运维的小工具希望用命令行的方式来使用,但是搜遍网上很多资料都没有系统讲解从开发、...

TensorFlow——Checkpoint为模型添加检查点的实例

TensorFlow——Checkpoint为模型添加检查点的实例

1.检查点 保存模型并不限于在训练模型后,在训练模型之中也需要保存,因为TensorFlow训练模型时难免会出现中断的情况,我们自然希望能够将训练得到的参数保存下来,否则下次又要重新训练...