web-dev-qa-db-ja.com

In Pythonエラーがなくなるまで試してください

Pythonにコードがあります。これは、サーバーにアクセスしているため確率的にエラーを引き起こしているようです。サーバーには500の内部サーバーエラーがあります。私の解決策は:

while True:
    try:
        #code with possible error
    except:
         continue
    else:
         #the rest of the code
         break

これは私にとってハックのようです。これを行うためのよりPython的な方法はありますか?

53
murgatroid99

それはあまりきれいになりません。これはあまりきれいなことではありません。せいぜい(breakの条件はwhileで上にあるので、とにかく読みやすいでしょう)、変数result = Noneを作成し、is None。また、変数を調整する必要があり、continueを意味的に正しいと思われるpassに置き換えて(エラーが発生しても構いません。無視したいだけです)、break-これは、ループから一度だけ実行される残りのコードも取得します。また、裸のexcept:句は、理由により悪であることに注意してください ドキュメントで指定

上記のすべてを組み込んだ例:

result = None
while result is None:
    try:
        # connect
        result = get_data(...)
    except:
         pass
# other code that uses result but is not involved in getting it
50
user395760

たぶんこのようなもの:

connected = False

while not connected:
    try:
        try_connect()
        connected = True
    except ...:
        pass
23
mouad

これは、4回の試行後にハードに失敗し、試行間で2秒待機するものです。希望に応じて変更して、このフォームから必要なものを取得します。

from time import sleep

for x in range(0, 4):  # try 4 times
    try:
        # msg.send()
        # put your logic here
        str_error = None
    except Exception as str_error:
        pass

    if str_error:
        sleep(2)  # wait for 2 seconds before trying to fetch the data again
    else:
        break

バックオフの例を次に示します。

from time import sleep

sleep_time = 2
num_retries = 4
for x in range(0, num_retries):  
    try:
        # put your logic here
        str_error = None
    except Exception as str_error:
        pass

    if str_error:
        sleep(sleep_time)  # wait before trying to fetch the data again
        sleep_time *= 2  # Implement your backoff algorithm here i.e. exponential backoff
    else:
        break
18
radtek

_itertools.iter_except_ レシピは、「例外が発生するまで関数を繰り返し呼び出す」という考え方をカプセル化しています。受け入れられた答えに似ていますが、レシピは代わりに反復子を提供します。

レシピから:

_def iter_except(func, exception, first=None):
    """ Call a function repeatedly until an exception is raised."""
    try:
        if first is not None:
            yield first()            # For database APIs needing an initial cast to db.first()
        while True:
            yield func()
    except exception:
        pass
_

確かに、後者のコードを直接実装できます。便宜上、このレシピを実装する別のライブラリ _more_itertools_ を使用します(オプション)。

コード

_import more_itertools as mit

list(mit.iter_except([0, 1, 2].pop, IndexError))
# [2, 1, 0]
_

詳細

ここでは、popが発生するまで、リストオブジェクトのすべての反復に対してIndexErrorメソッド(または指定された関数)が呼び出されます。

あなたの場合、_connect_function_と予想されるエラーがあれば、例外が発生するまで関数を繰り返し呼び出す反復子を作成できます。

_mit.iter_except(connect_function, ConnectionError)
_

この時点で、ループするかnext()を呼び出すことにより、他のイテレーターとして扱います。

3
pylang

これは、成功するまで再試行をラップするために作成したユーティリティ関数です。同じ基本構造を使用しますが、繰り返しを防ぎます。最終試行で例外を比較的簡単にキャッチして再スローするように変更できます。

def try_until(func, max_tries, sleep_time):
    for _ in range(0,max_tries):
        try:
            return func()
        except:
            sleep(sleep_time)
    raise WellNamedException()
    #could be 'return sensibleDefaultValue'

このように呼び出すことができます

result = try_until(my_function, 100, 1000)

引数をmy_functionに渡す必要がある場合は、try_untilに引数を転送するか、引数なしのラムダでラップすることでこれを行うことができます。

result = try_until(lambda : my_function(x,y,z), 100, 1000)
3
Harry Harrison

たぶんデコレーターベース?デコレータの引数として、再試行する例外のリストや試行回数を渡すことができます。

def retry(exceptions=None, tries=None):
    if exceptions:
        exceptions = Tuple(exceptions)
    def wrapper(fun):
        def retry_calls(*args, **kwargs):
            if tries:
                for _ in xrange(tries):
                    try:
                        fun(*args, **kwargs)
                    except exceptions:
                        pass
                    else:
                        break
            else:
                while True:
                    try:
                        fun(*args, **kwargs)
                    except exceptions:
                        pass
                    else:
                        break
        return retry_calls
    return wrapper


from random import randint

@retry([NameError, ValueError])
def foo():
    if randint(0, 1):
        raise NameError('FAIL!')
    print 'Success'

@retry([ValueError], 2)
def bar():
    if randint(0, 1):
        raise ValueError('FAIL!')
    print 'Success'

@retry([ValueError], 2)
def baz():
    while True:
        raise ValueError('FAIL!')

foo()
bar()
baz()

もちろん、「try」の部分は、両方のループで使用するため、別の機能に移動する必要がありますが、これは単なる例です;)

2
virhilo
e = ''
while e == '':
    try:
        response = ur.urlopen('https://https://raw.githubusercontent.com/MrMe42/Joe-Bot-Home-Assistant/mac/Joe.py')
        e = ' '
    except:
        print('Connection refused. Retrying...')
        time.sleep(1)

これは動作するはずです。 eを ''に設定し、whileループはまだ ''であるかどうかを確認します。 tryステートメントでエラーが検出された場合、接続が拒否されたことを出力し、1秒待ってからやり直します。 tryにエラーがなくなるまで続けられ、eが ''に設定され、whileループが強制終了されます。

1
user9311010

他のほとんどの人と同じように、私は有限回数試行し、試行間で寝ることをお勧めします。この方法では、リモートサーバーに実際に何かが発生した場合に無限ループに陥ることはありません。

また、期待する特定の例外が発生した場合にのみ続行することをお勧めします。これにより、予期しない例外を処理できます。

from urllib.error import HTTPError
import traceback
from time import sleep


attempts = 10
while attempts > 0:
    try:
        #code with possible error
    except HTTPError:
        attempts -= 1
        sleep(1)
        continue
    except:
        print(traceback.format_exc())

    #the rest of the code
    break

また、elseブロックは必要ありません。 exceptブロックで継続するため、tryブロックが機能するか、while条件が満たされるか、HTTPError以外の例外が発生するまで、ループの残りをスキップします。

1
noname

エラーのために再試行するときは、常に次のことを行う必要があります。

  • 再試行制限を実装するか、無限ループでブロックされる可能性があります
  • 遅延を実装します。そうしないと、CPUや既に苦しんでいるリモートサーバーなどのリソースを強く打ちすぎます。

これらの懸念をカバーしながらこの問題を解決する簡単な一般的な方法は、 backoff ライブラリを使用することです。基本的な例:

import backoff

@backoff.on_exception(
    backoff.expo,
    MyException,
    max_tries=5
)
def make_request(self, data):
    # do the request

このコードは、再試行ロジックを実装するデコレーターでmake_requestをラップします。特定のエラーMyExceptionが発生するたびに、再試行を5回に制限して再試行します。 指数バックオフ は、このコンテキストでは、リモートサーバーでの再試行による追加の負荷を最小限に抑えるのに役立ちます。

0
Brad Koch

以下は、エラーを文字列としてキャプチャするために使用する短いコードです。成功するまで再試行します。これはすべての例外をキャッチしますが、必要に応じて変更できます。

start = 0
str_error = "Not executed yet."
while str_error:
    try:
        # replace line below with your logic , i.e. time out, max attempts
        start = raw_input("enter a number, 0 for fail, last was {0}: ".format(start))
        new_val = 5/int(start)
        str_error=None
    except Exception as str_error:
         pass

警告:このコードは、例外が発生しなくなるまで永久ループに留まります。これは単純な例であり、MIGHTでは、ループを早く終了するか、再試行の間にスリープする必要があります。

0
radtek