廖雪峰的Python-装饰器

来源:互联网 发布:大数据工程师医疗 编辑:程序博客网 时间:2024/06/05 14:09

一、装饰器(Decorator)


1、定义

  在函数运行期间动态的增加函数的功能而又不改变函数的定义的方法叫做装饰器,装饰器的本质是高阶的返回函数。


例1:在函数前做到打印日志的功能。


#!/usr/bin/env python#-*- coding: utf-8 -*-def log(func):def wrapper(*args, **kw): print 'call %s():' % func.__name__ # %s是占位符return func(*args, **kw)  # 结束函数wrapper的运行,并将func函数值返回给wrapper。return wrapper # 结束函数log的运行,将函数返回到wrapper。@logdef now():print'2017-07-16'now()


  注:由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数。

wrapper()函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用。在wrapper()函数内,首先打印日志,再紧接着调用原始函数。


例2:自定义装饰器的文本


#!/usr/bin/env python #-*- coding: utf-8 -*-def log(text):def decorator(func):def wrapper(*args, **kw):print '%s %s():'%(text,func.__name__)return func(*args, **kw)return wrapperreturn decorator@log('execute')def now():print '2017-06-21'now()


  注:这样写有一个缺点就是在程序运行完之后,函数的名称改变了,所以,需要把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。

  Python内置的functools.wraps可以实现上述功能,具体使用方法如下。


例3:


#!/usr/bin/env python #-*- coding: utf-8 -*-import functools#导入模块def log(text):def decorator(func):@functools.wraps(func) #调用模块def wrapper(*args, **kw):print '%s %s():'%(text,func.__name__)return func(*args, **kw)return wrapperreturn decorator@log('execute')def now():print '2017-06-21'now()


二、练习题

习题1:请编写一个decorator,能在函数调用的前后打印出'begin call'和'end call'的日志。


#!/usr/env/bin python#-*- coding: utf-8 -*-import functoolsdef log(func): @functools.wraps(func) def wrape(*args,**kw): print 'begin call' func(*args,**kw) print 'end call' return return wrape@logdef now():print '2017年7月10日09:50:45'now()

  注:如果按照以前的写法,简单的在return func()后面加上 print ‘end call’程序是不会执行后面的语句的,这里要理解return的含义,即:终止函数的运行,并返回值。

  我犯的另一个错误就是,在return后加上 func,导致在返回值的时候再次运行了return 中的函数语句。这个问题的详细解释:https://segmentfault.com/q/1010000010204444?_ea=2203069


习题2:写出一个@log的decorator,使它既支持:


@log
def f():
pass
又支持:

@log('execute')
def f():
pass


#!/usr/bin/env python #-*- coding: utf-8 -*-import functoolsdef log(text):    if isinstance(text,str):                                                def decorator(func):            @functools.wraps(func)            def wrapper(*args,**kw):                                   if text:                                        print'%s %s(): '%(text,func.__name__)                else:                                        print'call %s(): '% func.__name__                func(*args,**kw)                        return                                return wrapper                         return decorator                    else:        @functools.wraps(text)                    def wrapper(*args,**kw):                       print'call %s(): '%text.__name__            text(*args,**kw)                        return        return wrapper @logdef f1():    print 'testing1' @log('')def f2():    print 'testing2' @log('excute')def f3():    print 'testing3'f1()f2()f3()



同时实现上述两个问题的代码。

#!/usr/bin/env python #-*- coding: utf-8 -*-import functoolsdef log(obj):    if isinstance(obj,str):        #判断参数obj是否为字符串,若不是,则其为函数参数,执行else内容                                #若是字符串,说明其为带参数的decorator        def decorator(func):            @functools.wraps(func)            def wrapper(*args,**kw):                print('begin call')        #打印函数开始执行信息                if obj:                    #如果字符串不为空                    print('%s %s(): '%(obj,func.__name__))                else:                    #字符串为空时                    print('call %s(): '% func.__name__)                func(*args,**kw)        #不使用return来返回函数func,而是直接执行,这样可以在该句后面再打印信息                print('end call')        #打印函数执行结束信息                return                    # wrapper函数的返回            return wrapper                 #decorator函数的返回        return decorator                #log函数的返回    else:        @functools.wraps(obj)            #obj即为函数名称        def wrapper(*args,**kw):            print('begin call')            print('call %s(): '%obj.__name__)            obj(*args,**kw)            print('end call')            return        return wrapper @logdef fnc():    print('I\'m fnc for testing log without parameter') @log('')def fnc2():    print('I\'m fnc2 for testing log with empty parameter') @log('excute')def fnc3():     print('I\'m fnc3 for testing log with parameter') fnc()print('fnc.__name__ = %s' % fnc.__name__)print('----------------------------------')fnc2()print('fnc2.__name__ = %s' % fnc2.__name__)print('----------------------------------')fnc3()print('fnc3.__name__ = %s' % fnc3.__name__)


原文:http://www.cnblogs.com/codingmengmeng/p/5811565.html




2017年7月16日17:38:37