Python装饰器

2017-09-05  本文已影响0人  ericsaid

Python装饰器

  1. 装饰器的本质是什么?

装饰器等价于高阶函数,形如myfunc=decorator(myfunc),接受函数或类作为输入,以函数或类作为输出。在@语法糖中,并没有要求输出为一个函数或者类。即

def decorator(func):
      return None

@decorator
def foo():
    pass

也可以作为@decorator语法装饰函数或类。
所以,可以将装饰器当成是一个接受函数或类为输入的函数即可。

  1. 装饰器的使用场景
  1. 编写不带装饰器参数的装饰器
def decorator(func):
  # do something when decorator called
    def _deco(*args, **kw):
        # do something before func called
        ret = func(*args, **kw)
        # do something after func called
        return ret
    return _deco

@decorator
def myfunc(*args, **kw):
    print "myfunc called"
    return True

内部函数“_deco”和myfunc的参数结构需要一致,当然不是要求形式上的一致。如参数为类的装饰器,如下

def singleton(cls):
    # do something when decorator called
    instances = {}

    def _getinstance(*args, **kw):
        if cls not in instances:
            instances[cls] = cls(*args, **kw)
        return instances[cls]
    return _getinstance

@singleton
class Myclass(object):
    def __init__(self, *args, **kw):
        pass

以上示例为构建单例模式的装饰器版本。

  1. 编写带装饰器参数的装饰器
    带参数的装饰器,等价于形如myfunc=decorator(a, b)(myfunc)的函数调用。decorator(a, b)返回一个不带装饰器参数的装饰器,即过程为:decorator1 = decorator(a, b) ;myfunc = decorator1(myfunc)。形式上多了一层函数,不过也可以使用柯里化的方式构建decorator1。如下例:
from functools import partial

def decorator(a, func):
    # do something when decorator called
    def _deco(*args, **kw):
        # do something before func called
        ret = func(*args, **kw)
        # do something after func called
        return ret
    return _deco
# or

def decorator_out(*args, **kwargs):
    # do something when decorator_out called with parameters
    def _outer(func):
        # do something when decorator_out called
        def _deco(*arg, **kw):
            # do something before func called
            # of course `*args` or `**kwargs`
            ret = func(*arg, **kw)
            # do something after func called
        return _deco
    return _outer

@partial(decorator, "param1")
def myfunc():
    print "myfunc"

@decorator_out("params1")
def myfunc1():
  print "myfunc1"
  1. functools.wraps
    因为Python中__name____doc__等metadata是在函数定义中生成的,而无法在执行过程中生成,所以myfunc=decorator(myfunc)执行之后,myfunc的metadata会发生变化,如myfunc.__name__,解决方案是装饰器中,加上functools.wraps。如上例:
from functools import wraps

def decorator_out(*args, **kwargs):
    # do something when decorator_out called with parameters
    def _outer(func):
        # do something when decorator_out called
        @wraps(func)
        def _deco(*arg, **kw):
            # do something before func called
            # of course `*args` or `**kwargs`
            ret = func(*arg, **kw)
            # do something after func called
            return ret
        return _deco
    return _outer
上一篇下一篇

猜你喜欢

热点阅读