2017-09-29 8 views
0

私はPythonで操作しようとしていますが、失敗した場合はを特定の方法で受け取ります。 10回まで再試行します。それ以外の方法でが失敗した場合すぐに失敗したい。 10回の再試行後、私はすべての失敗を発信者に伝えたい。条件付きで操作を再試行するためのPythonフローコントロール

私はフローコントロールを満足のいく方法でコーディングすることができませんでした。ここでは行動の一例だ私がしたい(ただし、スタイル!):

def run(): 
    max_retries = 10 
    for retry_index in range(max_retries): 
     try: 
      result = run_operation() 
     except OneTypeOfError: 
      if retry_index < max_retries - 1: 
       continue 
      else: 
       raise 

     if result.another_type_of_error: 
      if retry_index < max_retries - 1: 
       continue 
      else: 
       raise AnotherTypeOfError() 

     try: 
      result.do_a_followup_operation() 
     except AThirdTypeOfError: 
      if retry_index < max_retries - 1: 
       continue 
      else: 
       raise 

     return result 

    raise Exception("We don't expect to end up here") 

が最初に私は私はちょうどこれがそう再試行ロジックは、エラー処理ロジックから分離しているリファクタリングと考えていました。問題は、たとえば、OneTypeOfErrorがresult.do_a_followup_operation()によって呼び出された場合、その場合は再試行したくないということです。私は上記の特定の状況でのみ再試行したい。

私はおそらく、結果を返す関数、発生した例外(もしあれば)、および再試行可能な例外かどうかを示すフラグにリファクタリングできると考えました。どういうわけか、それは私には上記よりもあまりエレガントではなかった。

私はここに役立つかもしれないPythonのフロー制御パターンがあるのだろうかと思います。

+1

あなたは[ブロックを除き、複数を持っている]ことができます(https://stackoverflow.com/questions/6095717/python-one-try-multiple-except)これは確かにはるかにエレガントな私には感じない – JETM

答えて

0

特定の例外クラスと再帰を使用して、少し乾燥させることができます。これらの線に沿ったSth:

class Retry(Exception): 
    def __init__(self, e): 
     super(Exception, self).__init__() 
     self.e = e 

def run(max_retries=10, exc=None): 
    if max_retries <= 0: 
     raise exc or Exception('Whatever') 
    try: 
     try: 
      result = run_operation() 
     except OneTypeOfError as e: 
      raise Retry(e) 
     if result.another_type_of_error: 
      raise Retry(AnotherTypeOfError()) 
     try: 
      result.do_a_followup_operation() 
     except AThirdTypeOfError as e: 
      raise Retry(e) 
    except Retry as r: 
     return run(max_retries=max_retries-1, exc=r.e) 
    else: 
     return result 

ある反復回数を持つ反復解は意味的に疑わしいと思われる。結局のところ、あなたは全部が成功し、再試行は後退です。そして再試行を使い果たしたことは、私にとっては基本的なケースのように聞こえる。

+0

アハ - 感謝君は! – nonagon

0

編集:ああ、私はあなたのコードはJETM

で指摘したようにあなたは、ブロックを除く複数を使用しなかった示していることに気づいていませんでした。ここExceptionHandlingであなたの迅速なプライマーだ:

try: 
    # do something 
except OneTypeOfError as e: 
    # handle one type of error one way 
    print(e) # if you want to see the Exception raised but not raise it 
except AnotherTypeOfError as e: 
    # handle another type of error another way 
    raise e('your own message') 
except (ThirdTypeOfError, FourthTypeOfError) as e: 
    # handle error types 3 & 4 the same way 
    print(e) # if you want to see the Exception raised but not raise it 
except: # DONT DO THIS!!! 
    ''' 
    Catches all and any exceptions raised. 
    DONT DO THIS. Makes it hard to figure out what goes wrong. 
    ''' 
else: 
    # if the try block succeeds and no error is raisedm then do this. 
finally: 
    ''' 
    Whether the try block succeeds or fails and one of the except blocks is activated. 
    Once all those are done with, finally run this block. 
    This works even if your program crashed and so is great for cleaning up for example. 
    ''' 

私がしましたこれはかつてはずっと前のことでしたが、同じ種類の例外で同じ関数を再帰的に呼び出しましたが、別の例外はありませんでした。また、再試行インデックスとmax_retries変数を関数に渡しました。これはそれらをパラメータとして追加することを意味していました。

もう1つの方法は、ループ全体をmax_retriesのforループに置き、再試行したくない例外のブロックを除くすべてのブロックにブレークを追加することです。

最後に、forループの代わりに、すべてのものをwhileループに入れ、あるタイプの例外の場合は例外ブロックにインクリメント条件を挿入し、他の例外の場合はwhile例外条件をfalseにします。

0

私はこれを正しく理解していれば二つのことを変更することにより、以下のように、あなたがそれを行うことができます:

  • がダウンして0から10までの代わりに、retry_indexからtries_leftを数えるあなたは正の数であることを悪用し、より自然に読み取って、することができます真実。

  • あなたが変更された(またはラップ)した場合、それはすでにresult.another_errorがtrueの場合、最初の2つのexceptのブロックを組み合わせることができAnotherTypeOfErrorを引き上げるようrun_operation()。(あなたの代わりにif tries_leftをテストすることを選択した場合やcontinue後)

コードは、必要に応じてraiseelseを省略することで、わずかにより緻密化することができます - 制御フローは、とにかくその時点で流用される - 、と置くことによって、裸のものと同じ行の簡単な文ifelseなし。

for tries_left in range(10, -1, -1): 
    try: 
     result = run_operation() 
    except OneTypeOfError, AnotherTypeOfError: 
     if not tries_left: raise 
     continue 

    try: 
     result.do_a_followup_operation() 
    except AThirdTypeOfError: 
     if not tries_left: raise 
     continue 

    return result 
関連する問題