2013-12-13 7 views
3

У меня мало опыта работы с декораторами на Python, но я бы хотел написать декоратор функций, который запускает функцию, ловит конкретное исключение, и если исключение поймано, то повторная попытка функция определенное количество раз. То есть, я хотел бы сделать это:Исключение трапа, попробуйте еще раз в Python

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

Я полагаю, что такого рода вещи легко с декораторами, но я не ясно, о том, как именно это сделать.

ответ

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

+0

Принято для обертываний –

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

Вы могли бы адаптировать эту структуру для многих различных типов ошибок.

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 

Я полагаю, что по «2 повторения» вы подразумеваете, что операция будет предпринята 3 раза. В вашем примере есть несколько осложнений, которые могут затенять базовую настройку: Кажется, что вам нужен декоратор метода, так как первым параметром функции/метода является «self»; однако этот метод сразу же передаёт какой-нибудь плохой метод его параметра foo. Я сохранил эти осложнения :)