2016-04-20 13 views
1

私は、コードが動作するまで、または指定された回数の試行が完了するまで、何らかのコードを繰り返し実行しようとする小さなコンテキストマネージャを作成しようとしています。私はこれを記述しようとしましたが、コンテクストマネージャーに問題が発生したときに問題が発生しました:Pythonコンテキストマネージャはどのようにしてコードを実行できますか?

どのようにコードする必要がありますか?

import contextlib 
import random 

def main(): 

    with nolube(): 
     print(1/random.randint(0, 1)) 

@contextlib.contextmanager 
def nolube(
    tries = None # None: try indefinitely 
    ): 
    """ 
    Create a context for trying something repeatedly. 
    """ 
    tries_done = 0 
    rekt = True 
    if tries is None: 
     while rekt is True: 
      try: 
       yield 
       rekt = False 
      except: 
       tries_done += 1 
       pass 
    else: 
     while rekt is True and tries_done <= tries: 
      try: 
       yield 
       rekt = False 
      except: 
       tries_done += 1 
       pass 

if __name__ == "__main__": 
    main() 

答えて

3

@contextlib.contextmanagerは非常に明確な契約をしています。 のみ再開されます。コードを再実行するために使用することはできません。

実際、コンテキストマネージャを使用して、繰り返しを制御することはできません。コンテキストマネージャではなく、ここにループが必要です。コンテクストマネージャーはブロックを制御しません。コンテキストマネージャーはブロックの入力と終了時にのみ通知されます。

代わりにtenacity package*を使用してください。それはデコレータを提供します。あなたのために機能を再実行するデコレータwraps a function in a while True loop。あなたは、その関数を呼び出し、@retryで飾られ、関数にprint()文を移動することによって、あなたのケースに適用したい

*
import random 
from tenacity import retry 

@retry 
def foo(): 
    print(1/random.randint(0, 1)) 

def main(): 
    foo() 

この答えは、もともとretrying packageをお勧めしますが、そのプロジェクトが休止したときにはforked into a new package with updated APIでした。

+0

'retrying'パッケージはしばらく更新されていません。しかし、維持されている[靭性](https://pypi.python.org/pypi/tenacity)というフォークがあります。 – TimB

+0

@TimB:ヘッドアップありがとう、私は代わりに頑強さをお勧めする答えを編集しました。 –

1

短い答えは、あなたが実際にPythonのコンテキストマネージャでこれを行うことはできません。

yieldはコンテキストマネージャで1回しか使用できないため、をwhileループ内で使用することは意味がありません。これは、Rubyblockで使用されているyieldとは異なります。

また、コード本体にアクセスすることもできません。たとえば、再利用できるfunctionのようなものは自動的に取得されません。

再利用可能なretryロジックを実装する場合は、代わりに関数を使用します。

def retry(func, n_times=None): 
    i = 1 
    while True: 
     try: 
      return func() 
     except Exception: 
      i += 1 
      if n_times and i >= n_times: 
       raise 
2

これはできません。

  1. __enter__
  2. は、1つ以上の文
  3. コール__exit__

それは偉大なり3が起こることが保証されるポイントを、実行呼び出し:Pythonでコンテキストマネージャは、単にプロトコルですここでの重要な点はポイント2です:コンテキストマネージャはのコードをのコンテキスト内で実行し、プロセス3を実行します。その時点で、ラップされたコードはなくなります永遠に手に入れられて到達不能なので、「もう一度」と呼ぶことはできません。contextlibは関数としてそれをすることによって、単にあなたのコンテキストマネージャを定義するための素敵なAPIを提供しています:

@contextmanager 
def ctxt(): 
    # 1: __enter__ code 
    yield 
    # 3: __exit__ code 

とドキュメントはっきりspecifies

呼び出されたときに装飾された機能は、発電機、イテレータを返す必要があります。このイテレータは、まったく1つの値を生成する必要があります。これは、with文のas句がある場合は、それらのターゲットにバインドされます。

これまでの点はそのままです。あなたは repeteadly何かを呼び出すために何ができるか

は、関数に入れて、あなたの「繰り返し成功するまで」ロジックとその機能を飾ることです。

def dec(f): 
    def decorated(*args, **kwargs): 
     while True: 
      try: 
       return f(*args, **kwargs) 
      except: 
       pass 
    return decorated 

をしかし、これはとは全く無関係ですコンテキストマネージャー。

関連する問題