函数是一个引用类型,函数对象可以被赋予给变量,然后通过变量调用:
函数对象有一个__name__属性,可以获得函数的名字:
如果我们现在要增强now()函数的功能(比如在执行now函数前打印日志),但是又不想修改now()函数,在代码运行期间动态增加功能的方式,叫做装饰器。
本质上,装饰器(decorator)就是一个"返回值是一个函数"的高阶函数,满足打印日志的装饰器示例如下:
def log(func):
def wrapper(*args, **kw):
print 'call %s():' % func.__name__
return func(*args, **kw)
return wrapper
上面的装饰器接收一个函数作为参数,并返回一个函数,我们使用@语法,把decorator置于函数定义处:
@log
def now():
print '20160831'
调用函数now,执行如下:
把@log放到now()函数的定义处,相当于执行了语句:
now = log(now)
也就是now变量重新进行了赋值,返回值是wrapper函数,先打印日志,然后再调用原始函数。
如果装饰器本身需要传入参数,那就需要编写一个返回装饰器的高阶函数,如下:
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print '%s %s():' % (text, func.__name__)
return func(*args, **kw)
return wrapper
return decorator
这个三层嵌套的装饰器用法如下:
@log('execute')
def now():
print '2013-12-25'
执行结果如下:
>>> now()
execute now():
2013-12-25
三层嵌套相当于执行:
>>> now = log('execute')(now)
首先执行log('execute')
,返回的是decorator
函数,再调用返回的函数,参数是now
函数,返回值最终是wrapper
函数。
decorator装饰之后的函数,它们的__name__
已经从原来的'now'
变成了'wrapper'
:
>>> now.__name__
'wrapper'
因为返回的那个wrapper()
函数名字就是'wrapper'
,所以,需要把原始函数的__name__
等属性复制到wrapper()
函数中,否则,有些依赖函数签名的代码执行就会出错。
使用Python内置的functools.wraps
,完整的装饰器如下:
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print 'call %s():' % func.__name__
return func(*args, **kw)
return wrapper
或者带参数的装饰器:
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 wrapper
return decorator