装饰器
-----在代码运行期间动态的增加功能的方式,本质上是一个返回函数的高阶函数
功能:插入场景,性能测试,事务处理,达到代码重用的目的
函数是一个对象,可赋值给变量:
def time():
print'2017-7-19'
f = time
f()
>>> 2017-7-19
函数对象的
_name_函数,可以拿到函数的名字:
print time.__name__
print f.__name__
>>>time
>>>time
初步定义一个简单的装饰器:
def deco(func):
print 'my %s():'%func.__name__
def time():
print '2017-7-9'
deco(time)
time()
>>>my time():
>>>2017-7-9
缺点:所有的“time”调用处都要改为“deco(time)”
改进:避免装饰器deco对“time”函数的调用代码的影响,增加一个内嵌函数wrapper
ef deco(func):#装饰器接受一个函数作为参数,并返回一个函数
def wrapper():#wrapper-->包装
print 'my %s():'%func.__name__
return func()
return wrapper
def time():
print'2017-7-9'
time = deco(time)
time()
>>>my time():
>>>2017-7-9
装饰器语法糖:用“
@”语法糖来精简装饰器的代码:
def deco(func):
def wrapper():
print 'my %s():'%func.__name__
return func()
return wrapper
@deco#相当于time = deco(time),则不需要额外代码来给“time”重新赋值
def time():
print '2017-7-9'
time()
>>>my time():
>>>2017-7-9
两层嵌套--->被装饰的函数带参数:
def deco(func):
def wrapper(a,b):#在内嵌函数中加上同样的参数
print 'a + b =',a + b
return func(a,b)
return wrapper
@deco#相当于time = deco(time(1,2))
def time(a,b):
print '2017-7-9'
time(1,2)
>>>my time():
>>>2017-7-9
>>>a + b = 3
>>>2017-7-9
???如果多个函数拥有不同的参数形式,怎么共用同样的装饰器:
在Python中,函数支持(*args,**kw)可变参数,所以内嵌函数可以通过可变参数来实现
三层嵌套--->带参数的装饰器:
def deco_(text):
def deco(func):
def wrapper(*args,**kw):
print '%s %s():'%(text,func.__name__)
return func(*args,**kw)
return wrapper
return deco
@deco_('my')#相当于deco_(’my‘)(time)
def time():
print '2017-7-9'
time()
>>>my time():
>>>2017-7-9
程序分析:首先执行deco_('my'),返回的是deco函数,再调用返回的函数,参数是time函数,返回值最终是wrapper函数
因此,返回的名字由time变为wrapper:
def deco(func):
def wrapper(*args,**kw):
print '%s'%func.__name__
return func(*args,**kw)
return wrapper
@deco
def time():
print time.__name__
time()
>>>time
>>>wrapper
因此,需要把原始函数的_name_等属性复制到wrapper函数中,否则,有些依赖函数签名的代码执行就会出错
因为Python中有内置的函数functools.wraps代替了wrapper._name_ = func._name_ ,则只需要在内嵌函数wrapper前面加上“@functools.wraps(func)”
import functools
def deco(func):
@functools.wraps(func)
def wrapper(*args,**kw):
print '%s' %func.__name__
return func(*args,**kw)
return wrapper
@deco
def time():
print time.__name__
time()
>>>time
>>>time
调用顺序:与声明的顺序相反
def deco_1(func): print '调用deco_1' def wrapper(*args,**kw): print '调动deco_1的wrapper' return func(*args,**kw) return wrapper def deco_2(func): print '调用deco_2' def wrapper(*args,**kw): print '调用deco_2的wrapper' return func(*args,**kw) return wrapper @deco_1 @deco_2 def order(): pass order()#即deco_1(deco_2(order)) >>>调用deco_2
>>>调用deco_1 >>>调动deco_1的wrapper >>>调用deco_2的wrapper
习题:
习题一:请编写一个decorator,能在函数调用的前后打印出
'begin call'
和'end call'
的日志。习题二:能否写出一个@log的deco,使它既支持:@log 又支持: @log('test')import functools def deco(func): @functools.wraps(func) def wrapper(*args, **kw): print 'begin call %s()'%func.__name__ func(*args, **kw) print 'end call %s()' % func.__name__ #return func(*args, **kw) return wrapper @deco def diary(): print '2017-7-9' diary() >>>begin call diary() >>>2017-7-9 >>>end call diary()
def f(): def f():
pass pass
def log(*text): def deco(func): @functools.wraps(func) def wrapper(*args, **kw): if len(text) == 0: print'log无输入参数' else: for i in text: print'log输入了参数:%s'%i return func(*args, **kw) return wrapper return deco @log('my','deco') @log() def test(): pass test() >>>log输入了参数:my >>>log输入了参数:deco >>>log无输入参数