【问题标题】:Trap exception, try again decorator in Python捕获异常,在 Python 中重试装饰器
【发布时间】:2013-12-31 18:37:53
【问题描述】:

我对 Python 中的装饰器几乎没有经验,但我想编写一个函数装饰器来运行该函数,捕获特定异常,如果捕获到异常,则重试该函数一定次数。也就是说,我想这样做:

@retry_if_exception(BadStatusLine, max_retries=2)
def thing_that_sometimes_fails(self, foo):
   foo.do_something_that_sometimes_raises_BadStatusLine()

我认为这种事情对装饰器来说很容易,但我不清楚具体如何去做。

【问题讨论】:

    标签: python exception decorator python-decorators


    【解决方案1】:
    from functools import wraps
    def retry_if_exception(ex, max_retries):
        def outer(func):
            @wraps(func)
            def wrapper(*args, **kwargs):
                assert max_retries > 0
                x = max_retries
                while x:
                    try:
                        return func(*args, **kwargs)
                    except ex:
                        x -= 1
            return wrapper
        return outer
    

    看看为什么you better use @wraps

    【讨论】:

    • 接受换行说明
    【解决方案2】:

    我认为你基本上想要这样的东西:

    def retry_if_exception(exception_type=Exception, max_retries=1):
        def decorator(fn):
            def wrapper(*args, **kwargs):
                for i in range(max_retries+1):
                    print('Try #', i+1)
                    try:
                        return fn(*args, **kwargs)
                    except exception_type as e:
                        print('wrapper exception:', i+1, e)
            return wrapper
        return decorator
    
    @retry_if_exception()
    def foo1():
        raise Exception('foo1')
    
    @retry_if_exception(ArithmeticError)
    def foo2():
        x=1/0
    
    @retry_if_exception(Exception, 2)
    def foo3():
        raise Exception('foo3')
    

    【讨论】:

      【解决方案3】:

      作为大纲,你会按照以下方式做一些事情:

      import random
      
      def shaky():
          1/random.randint(0,1)
      
      def retry_if_exception(f):
          def inner(retries=2):
              for retry in range(retries):
                  try:
                      return f()
                  except ZeroDivisionError:
                      print 'try {}'.format(retry)
              raise         
      
          return inner            
      
      @retry_if_exception
      def thing_that_may_fail():
          shaky()
      
      thing_that_may_fail() 
      

      正如所写,这将失败大约 1/2 的时间。

      当它失败时,打印:

      try 0
      try 1
      Traceback (most recent call last):
        File "Untitled 2.py", line 23, in <module>
          thing_that_may_fail()    
        File "Untitled 2.py", line 10, in inner
          return f()
        File "Untitled 2.py", line 21, in thing_that_may_fail
          shaky()
        File "Untitled 2.py", line 4, in shaky
          1/random.randint(0,1)
      ZeroDivisionError: integer division or modulo by zero
      

      您可以使这种结构适应许多不同类型的错误。

      【讨论】:

        【解决方案4】:

        以下内容似乎符合您的描述:

        def retry_if_exception( exception, max_retries=2 ):
            def _retry_if_exception( method_fn ):
                # method_fn is the function that gives rise
                # to the method that you've decorated,
                # with signature (slf, foo)
                from functools import wraps
                def method_deco( slf, foo ):
                    tries = 0
                    while True:
                        try:
                            return method_fn(slf, foo)
                        except exception:
                            tries += 1
                            if tries > max_retries:
                                raise
                return wraps(method_fn)(method_deco)
            return _retry_if_exception
        

        这是一个使用示例:

        d = {}
        
        class Foo():
            def usually_raise_KeyError(self):
                print("d[17] = %s" % d[17])
        
        foo1 = Foo()
        
        class A():
            @retry_if_exception(KeyError, max_retries=2)
            def something_that_sometimes_fails( self, foo ):
                print("About to call foo.usually_raise_KeyError()")
                foo.usually_raise_KeyError()
        
        a = A()
        a.something_that_sometimes_fails(foo1)
        

        这给出了:

        About to call foo.usually_raise_KeyError()
        About to call foo.usually_raise_KeyError()
        About to call foo.usually_raise_KeyError()
        Traceback (most recent call last):
          File " ......... TrapRetryDeco.py", line 39, in <module>
            a.something_that_sometimes_fails( foo1)
          File " ......... TrapRetryDeco.py", line 15, in method_deco
            return method_fn( slf, foo)
          File " ......... TrapRetryDeco.py", line 36, in something_that_sometimes_fails
            foo.usually_raise_KeyError()
          File " ......... TrapRetryDeco.py", line 28, in usually_raise_KeyError
            print("d[17] = %s" % d[17])
        KeyError: 17
        

        我假设“2 次重试”是指该操作将被尝试 3 次。您的示例有一些复杂性可能会掩盖基本设置: 看来您想要一个方法装饰器,因为您的函数/方法的第一个参数是“self”;但是,该方法会立即委托给其 foo 参数的某些错误方法。我保留了这些并发症:)

        【讨论】:

          猜你喜欢
          • 2019-10-05
          • 2013-08-22
          • 2016-06-16
          • 2011-05-14
          • 2019-06-23
          • 1970-01-01
          • 2016-05-12
          • 1970-01-01
          • 2017-09-05
          相关资源
          最近更新 更多