装饰器
-----在代码运行期间动态的增加功能的方式,本质上是一个返回函数的高阶函数
功能:插入场景,性能测试,事务处理,达到代码重用的目的
函数是一个对象,可赋值给变量:
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'的日志。
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()
习题二:能否写出一个@log的deco,使它既支持:@log 又支持: @log('test')
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无输入参数
1003

被折叠的 条评论
为什么被折叠?



