目录
- 简单装饰器
- 修饰带参函数的装饰器
- 本身带参数的装饰器
- 类装饰器
- 装饰器缺点
- 装饰器用途
闭包
说到装饰器就不能忽略闭包,下面先介绍一下闭包的概念:
在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。闭包可以用来在一个函数与一组“私有”变量之间创建关联关系。在给定函数被多次调用的过程中,这些私有变量能够保持其持久性。—— 维基百科
简单来说,就是函数内部套函数,里面的函数用到了外面函数的变量,然后把里面的函数当作对象返回,比如下面的例子:
def Outer(msg):
def Iner():
print msg # msg是外部函数的变量(外部变量)
return printer # 返回的是函数,同时延长了外部变量的生命周期
printer = Outer('test')
printer()
上面这个例子展示了基本的闭包,同时闭包也可以下面这样用:
def adder(x):
def wrapper(y):
return x + y
return wrapper
adder5 = adder(5)
# 输出 15
adder5(10)
# 输出 11
adder5(6)
这时我们避免了使用全局变量,使得外部函数的参数x的生命周期得到延长。
装饰器
装饰器其实就是由闭包实现的,下面给出几种不同的装饰器:
- 最简单的装饰器:
def Outer(func):
def wrapper():
logging.warn("%s is running" % func.__name__)
return func()
return wrapper
@Outer
def foo():
print("hello world")
foo()
- 修饰带参数函数的装饰器:
def Outer(func):
def wrapper(*args, **kwargs):
if level == "warn":
logging.warn("%s is running" % func.__name__)
elif level == "info":
logging.info("%s is running" % func.__name__)
return func(*args)
return wrapper
return decorator
@Outer
def foo(name):
print("hello world %s" % name)
foo('silly b')
- 装饰器本身带参数:
def Outer(level):
def decorator(func):
def wrapper(*args, **kwargs):
if level == "warn":
logging.warn("%s is running" % func.__name__)
elif level == "info":
logging.info("%s is running" % func.__name__)
return func(*args)
return wrapper
return decorator
@Outer(level="warn")
def foo(name):
print("hello world %s" % name)
foo(‘silly b’)
- 类装饰器:
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()
使用类装饰器主要依靠类的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。
- 类装饰器顺序:
@a
@b
@c
def f ():
pass
# 调用顺序从下到上,c——b——a:
f = a(b(c(f)))
- 装饰器缺点:
会丢失原函数信息,__docstring__,__name__等都变成wrapper函数的信息了,这时我们需要使用functools.wraps,wraps本身也是一个装饰器,它能把原函数的元信息拷贝到装饰器里面的 func 函数中,这使得装饰器里面的 func 函数也有和原函数有一样的元信息了。
from functools import wraps
def logged(func):
@wraps(func)
def with_logging(*args, **kwargs):
print func.__name__ # 输出 'f'
print func.__doc__ # 输出 'does some math'
return func(*args, **kwargs)
return with_logging
@logged
def f(x):
"""does some math"""
return x + x * x
- 装饰器一般用途如下:
- 注入参数(提供默认参数,生成参数)
- 记录函数行为(日志、缓存、计时等功能)
- 预处理/后处理(配置上下文)
- 修改调用时的上下文(线程异步或者并行,类方法)
参考文章: