1.最简单的装饰器不带入参

 

def  func():
pass

def decorate(func)
  def wrapper():
    return func()
  return wrapper
使用
@decorate
def aa(m):
pass

2.要是带参数就简单给他就是了:

因为函数有千千万,你只管你自己的函数,别人的函数参数是什么样子,鬼知道?还好Python提供了可变参数​​*args​​​和关键字参数​​**kwargs​​,有了这两个参数,装饰器就可以用于任意目标函数了。

def  decorate(func)
  def wrapper(*args,**kwargs):
    return func((*args,**kwargs)
  return wrapper

如何优化你的装饰器#

嵌套的装饰函数不太直观,我们可以使用第三方包类改进这样的情况,让装饰器函数可读性更好。

decorator.py#

​decorator.py​​​ 是一个非常简单的装饰器加强包。你可以很直观的先定义包装函数​​wrapper()​​​,再使用​​decorate(func, wrapper)​​方法就可以完成一个装饰器。

Copy

from decorator import decorate

def wrapper(func, *args, **kwargs):
"""print log before a function."""
print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__)
return func(*args, **kwargs)

def logging(func):
return decorate(func, wrapper) # 用wrapper装饰func

你也可以使用它自带的​​@decorator​​装饰器来完成你的装饰器。

Copy

from decorator import decorator

@decorator
def logging(func, *args, **kwargs):
print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__)
return func(*args, **kwargs)

​decorator.py​​​实现的装饰器能完整保留原函数的​​name​​​,​​doc​​​和​​args​​​,唯一有问题的就是​​inspect.getsource(func)​​​返回的还是装饰器的源代码,你需要改成​​inspect.getsource(func.__wrapped__)​​。

wrapt#

​wrapt​​​是一个功能非常完善的包,用于实现各种你想到或者你没想到的装饰器。使用wrapt实现的装饰器你不需要担心之前inspect中遇到的所有问题,因为它都帮你处理了,甚至​​inspect.getsource(func)​​也准确无误。

Copy

import wrapt

# without argument in decorator
@wrapt.decorator
def logging(wrapped, instance, args, kwargs): # instance is must
print "[DEBUG]: enter {}()".format(wrapped.__name__)
return wrapped(*args, **kwargs)

@logging
def say(something): pass

使用wrapt你只需要定义一个装饰器函数,但是函数签名是固定的,必须是​​(wrapped, instance, args, kwargs)​​​,注意第二个参数​​instance​​​是必须的,就算你不用它。当装饰器装饰在不同位置时它将得到不同的值,比如装饰在类实例方法时你可以拿到这个类实例。根据​​instance​​​的值你能够更加灵活的调整你的装饰器。另外,​​args​​​和​​kwargs​​也是固定的,注意前面没有星号。在装饰器内部调用原函数时才带星号。

如果你需要使用wrapt写一个带参数的装饰器,可以这样写。

Copy

def logging(level):
def wrapper(wrapped, instance, args, kwargs):
print "[{}]: enter {}()".format(level, wrapped.__name__)
return wrapped(*args, **kwargs)
return wrapper

@logging(level="INFO")
def do(work): pass

关于wrapt的使用,建议查阅官方文档,在此不在赘述。

小结#

Python的装饰器和Java的注解(Annotation)并不是同一回事,和C#中的特性(Attribute)也不一样,完全是两个概念。

装饰器的理念是对原函数、对象的加强,相当于重新封装,所以一般装饰器函数都被命名为​​wrapper()​​​,意义在于包装。函数只有在被调用时才会发挥其作用。比如​​@logging​​​装饰器可以在函数执行时额外输出日志,​​@cache​​装饰过的函数可以缓存计算结果等等。

而注解和特性则是对目标函数或对象添加一些属性,相当于将其分类。这些属性可以通过反射拿到,在程序运行时对不同的特性函数或对象加以干预。比如带有​​Setup​​​的函数就当成准备步骤执行,或者找到所有带有​​TestMethod​​的函数依次执行等等。

至此我所了解的装饰器已经讲完,但是还有一些内容没有提到,比如装饰类的装饰器。有机会再补充。谢谢观看。