因为高阶函数的存在,可以接受函数传参,这就让不改变原有函数的前提下,“装饰”原有函数成为可能。也就是增强其功能。
不带参数的装饰器实现不带参数的装饰器至少需要这样两步操作:
可以看出,不带参数的装饰器实现有两个步骤:
- 传入原函数名
- 传入原函数参数列表,增加功能代码,调用原函数
两个步骤之间的链接,是用return返回函数名进行指向(闭包)。
类比不带参数的装饰器实现步骤,每一次传参,就需要定义一次函数,并完成内外层函数的指向调用,类似与递归。
因此不难想象,当我们本身的装饰器也需要参入特定参数时,我们就需要把不带参数的装饰器再进行一次封装,在传入原函数前,增加一个传参和指向的过程。
如下:
可以看出,带参数的装饰器实现就多了一个传送装饰参数的过程,就多了一层。
不带参数的装饰器
def log_decorator(f):
def wrapper(*args,**kw):
print '%s()...'%f.__name__
return f(*args,**kw)
return wrapper
带参数的装饰器
def log(prefix):
def log_decorator(f):
def wrapper(*args,**kw):
print '[%s]%s()...'%(prefix,f.__name__)
return f(*args,**kw)
return wrapper
return log_decorator
从上面两个实例对比可以看出,带参数与不带参数,就是多了一层封装,多的这一层,提供了传参的入口。
类装饰器类装饰器,相比函数装饰器,具有灵活度大、高内聚、封装性等优点。
class Foo(object):
def __init__(self, func):
self._func = func
def __call__(self):
print 'class decorator runing'
self._func()
print 'class decorator ending'
@Foo
def bar():
print 'bar'
bar()
使用装饰器极大复用了代码,但是也有缺点就是原函数元信息不见了,比如函数的docstring、name、参数列表
装饰器
def logged(func):
def with_logging(*args, **kwargs):
print func.__name__ + 'was called'
return func(*args, **kwargs)
return with_logging
函数
@logged
def f(x):
"""does some math"""
return x + x*x
# 等价于f = logged(f)
不难发现,函数f被with_logging取代了,当然它的docstring,name就是变成了with_logging函数的信息了。
print f.__name__ # 'with_logging'
print f.__doc__ # 'None'
怎么解决,这里有个functools.wraps,wraps本身也是一个装饰器,它能把元函数的元信息拷贝到装饰器函数中,这使得装饰器函数也有和原函数一样的元信息了。
from functools import wraps
def logged(func):
@wraps(func)
def with_logging(*args, **kwargs):
print func.__name__ + 'was called'
return func(*args, **kwargs)
return with_logging
@logged
def f(x):
"""does some math"""
return x + x*x
print f.__name__ # 'f'
print f.__doc__ # 'does some math'
内置装饰器
@staticmethod
@classmethod
@property
装饰器的顺序@a
@b
@c
def f():
pass
# 等效于
f = a(b(c(f)))