陷阱异常,再试一次Python中的装饰器

时间:2013-12-13 04:12:18

标签: python exception decorator python-decorators

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

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

我认为装饰者很容易做到这一点,但我不知道如何去做。

4 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:1)

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

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')

答案 2 :(得分:0)

作为概要,你可以按照以下方式做点什么:

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

您可以将此结构调整为许多不同类型的错误。

答案 3 :(得分:0)

以下似乎做了您所描述的内容:

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

我认为通过&#34; 2重试&#34;你的意思是手术将被尝试3次全部告诉。您的示例有一些复杂性可能会掩盖基本设置: 你似乎想要一个方法装饰器,因为你的函数/方法的第一个参数是&#34; self&#34 ;;但是,该方法会立即委托其foo参数的一些不良方法。我保留了这些并发症:)