Python中的装饰器可以很方便的对已有的函数进行改写或者优化,从某种程度上可以将函数看成类,装饰器的作用就是类继承的过程。
def log(func):
def wrapper(*arg,**kw):
print('call %s' % func.__name__)
return func(*arg,**kw)
return wrapper
@log
def now():
print('2017-1-7')
now()
运行的结果是:
call now
2017-1-7
代码中的@
语法相当于now=log(now)
。我们可以将上面的代码写成下面的形式:
def log(func):
def wrapper(*arg,**kw):
print('call %s' % func.__name__)
return func(*arg,**kw)
return wrapper
def now():
print('2017-1-7')
log(now)()
结果依然是:
call now
2017-1-7
从上面两段代码可以看出@xxx
的作用等价于now=xxx(now)
,也就是log
是一个将紧跟他下面的函数作为参数的函数。无论函数xxx
里有多少层函数,跟在@后面的函数或其所指向的函数必须是以函数作为参数的。我们来看下面一段代码:
def log(text):
if isinstance(text,str):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
else:
def wrapper(*args, **kw):
print('call %s()' % text.__name__)
return text(*args, **kw)
return wrapper
@log
def now1():
print('2017-1-7')
@log('excute')
def now2():
print('2017-1-7')
now1()
now2()
运行结果如下:
call now()
2017-1-7
excute now():
2017-1-7
上面的这段代码中的装饰器,除了正常的功能外,还可以加参数,这是为什么呢?在函数log
中,先判断的是参数text
的类型,先不论text
是那种类型,函数log或其所指向的函数必须都是以函数作为参数的。@log
的作用等价于now1=log(now1)
,now1
是函数类型,不是字符串类型,所以执行的是这段代码:
def wrapper(*args, **kw):
print('call %s()' % text.__name__)
return text(*args, **kw)
return wrapper
在@xxx
中xxx
就是一个函数,因此@log('excute')
中log('excute')
就是一个函数,所以log('excute')
的作用等价于now2=log('excute')(now2)
。但是,'excute'
先传进去的所以经判断执行的是这段代码:
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
在这还有最后一个问题就是now
的__name__
属性会变成wrapper
,还是因为now2=log('excute')(now2)
。log('excute')(now2)
指向的函数就是wrapper
,因此完整的代码就是:
import functools
def log(text):
if isinstance(text,str):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
else:
@functools.wraps(text)
def wrapper(*args, **kw):
print('call %s()' % text.__name__)
return text(*args, **kw)
return wrapper
@log
def now1():
print('2017-1-7')
@log('excute')
def now2():
print('2017-1-7')
now1()
now2()
@functools.wraps(func)
的作用就是wrapper.__name__ = func.__name__
。