Python装饰器

来源:互联网 发布:中囯邮政网络培训学院 编辑:程序博客网 时间:2024/06/06 06:56

由于函数也是一个对象,而且函数对象可以被赋值给变量,所以通过该变量也能调用该函数。

def now():    print('hahah')f=nowf()
hahah

函数对象有一个_ _ name _ _属性,可以拿到该函数的名字

print f.__name__print now.__name__
nownow

现在假设我们要增强now函数的功能,比如,在打印之前自动打印日志,但又不希望改动now()函数定义,这种在代码运行期动态增加功能的方式,称之为装饰器(‘Decorator’)。
本质上,装饰器就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator,可以定义如下:

def log(func):    def wrapper(*args,**kw):        print('call %s():'%func.__name__)        func(*args,**kw)    return wrapper@logdef now():    print "2017-4-15"now()

log是一个装饰器,所以可以接受一个函数为参数,并返回一个参数。

call now():2017-4-15

把@log放到now函数定义的前面相当于执行科语句:

now=log(now)

由于log是一个装饰器返回一个函数,所以原来的函数依然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()中返回wrapper()函数。
wrapper的参数定义为*args,**kw意味着可以接任何参数。在wrapper内先打印日志,在执行now()函数。
如果decorator本身需要传递参数,那就需要编写一个返回decorator的高阶函数。

def log(text):    def decorator(func):        def wrapper(*args,**kw):            print '%s %s():'%(text,func.__name__)            func(*args,**kw)        return wrapper    return decorator@log('execute')def now():    print "2017-4-15"now()
execute now():2017-4-15

三层嵌套相当于

now=log('execute')(now)

首先执行log(‘execute’)返回decorator函数,在调用decorator函数参数为now函数返回值为wrapper函数。
以上两种定义都没错,但换差最后一步。每个函数对象都有_ _ name _ _属性,但是:

print now.__name__
wrapper

因为返回的wrapper()的名字就是wrapper,需要把now的函数属性复制到wra函数中,不要使用:

wrapper.__name__=naw.__name__

python内置的functool.wraps干的就是这个,所以完整的decorator是

import functoolsdef log(func):    @functools.wraps(func)    def wrapper(*args,**kw):        print('call %s():'%func.__name__)        func(*args,**kw)    return wrapper@logdef now():    print "2017-4-15"now() now.__name__

import functoolsdef log(text):    def decorator(func):        @functools.wraps(func)        def wrapper(*args,**kw):            print '%s %s():'%(text,func.__name__)            func(*args,**kw)        return wrapper    return decorator@log('execute')def now():    print "2017-4-15"print now.__name__

小结
在面对象(OOP)的设计模式中,decorator也称为装饰模式,OOP的装饰模式通过继承和组合实现,而python除了能支持OOP的装饰模式,直接从语法上支持decorator。python的decorator可以用函数实现也可以用类实现。

import functoolsdef log(obj):    if isinstance(obj,str):        text=obj        def decorator(func):            @functools.wraps(func)            def wrapper(*args,**kw):                print '%s %s():'%(text,func.__name__)                func(*args,**kw)            return wrapper        return decorator    else:        func=obj        @functools.wraps(func)        def wrapper(*args,**kw):            print '%s():'%func.__name__            func(*args,**kw)        return wrapper@log('execute')def now():    print "2017-4-15"now()@logdef now():    print "2017-4-15"now()
execute now():2017-4-15now():2017-4-15
0 0
原创粉丝点击