美文网首页
Python装饰器

Python装饰器

作者: ericsaid | 来源:发表于2017-09-05 12:18 被阅读0次

    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
    

    相关文章

      网友评论

          本文标题:Python装饰器

          本文链接:https://www.haomeiwen.com/subject/treujxtx.html