装饰器

装饰器

  • 最简装饰器

      def deco(func):
          def wrap(*args, **kwargs):
              return func(*args, **kwargs)
          return wrap
      @deco
      def foo(a, b):
          return a ** b
     
  • 原理

    • 对比被装饰前后的 foo.__name__foo.__doc__

        from functools import wraps
        def deco(func):
            '''i am deco'''
            @wraps(func)  # 还原被装饰器修改的原函数属性
            def wrap(*args, **kwargs):
                '''i am wrap'''
                return func(*args, **kwargs)
            return wrap
       
    • 简单过程

        fn = deco(func)
        foo = fn
        foo(*args, **kwargs)
       
    • 多个装饰器叠加调用的过程

        @deco1
        @deco2
        @deco3
        def foo(x, y):
            return x ** y
        # 过程拆解 1
        fn3 = deco3(foo)
        fn2 = deco2(fn3)
        fn1 = deco1(fn2)
        foo = fn1
        foo(3, 4)
        # 过程拆解 2
        # 单行: deco1( deco2( deco3(foo) ) )(3, 2)
        deco1(
            deco2(
                deco3(foo)
            )
        )(3, 4)
       
  • 带参数的装饰器

      def deco(n):
          def wrap1(func):
              def wrap2(*args, **kwargs):
                  return func(*args, **kwargs)
              return wrap2
          return wrap1
      # 调用过程
      wrap1 = deco(n)
      wrap2 = wrap1(foo)
      foo = wrap2
      foo()
      # 单行形式
      check_result(30)(foo)(4, 8)
     
  • 装饰器类和 __call__

      class Deco:
          def __init__(self, func):
              self.func = func
          def __call__(self, *args, **kwargs):
              return self.func(*args, **kwargs)
      @Deco
      def foo(x, y):
          return x ** y
      # 过程拆解
      fn = Deco(foo)
      foo = fn
      foo(12, 34)
     
  • 使用场景

    • 参数、结果检查

    • 缓存、计数

    • 日志、统计

    • 权限管理

    • 重试

    • 其他

  • 练习1: 写一个 timer 装饰器, 计算出被装饰函数调用一次花多长时间, 并把时间打印出来

      import time
      from functools import wraps
      def timer(func):
          @wraps(func)  # 修正 docstring
          def wrap(*args, **kwargs):
              time0 = time.time()
              result = func(*args, **kwargs)
              time1 = time.time()
              print(time1 - time0)
              return result
          return wrap
     
  • 练习2: 写一个 Retry 装饰器

      import time
      class retry(object):
          def __init__(self, max_retries=3, wait=0, exceptions=(Exception,)):
              self.max_retries = max_retries
              self.exceptions = exceptions
              self.wait = wait
          def __call__(self, func):
              def wrapper(*args, **kwargs):
                  for i in range(self.max_retries + 1):
                      try:
                          result = func(*args, **kwargs)
                      except self.exceptions:
                          time.sleep(self.wait)
                          continue
                      else:
                          return result
              return wrapper
     
posted @ 2018-10-08 16:39  gugubeng  阅读(126)  评论(0编辑  收藏  举报