Pythonにコードがあります。これは、サーバーにアクセスしているため確率的にエラーを引き起こしているようです。サーバーには500の内部サーバーエラーがあります。私の解決策は:
while True:
try:
#code with possible error
except:
continue
else:
#the rest of the code
break
これは私にとってハックのようです。これを行うためのよりPython的な方法はありますか?
それはあまりきれいになりません。これはあまりきれいなことではありません。せいぜい(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
たぶんこのようなもの:
connected = False
while not connected:
try:
try_connect()
connected = True
except ...:
pass
これは、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
_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()
を呼び出すことにより、他のイテレーターとして扱います。
これは、成功するまで再試行をラップするために作成したユーティリティ関数です。同じ基本構造を使用しますが、繰り返しを防ぎます。最終試行で例外を比較的簡単にキャッチして再スローするように変更できます。
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)
たぶんデコレーターベース?デコレータの引数として、再試行する例外のリストや試行回数を渡すことができます。
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」の部分は、両方のループで使用するため、別の機能に移動する必要がありますが、これは単なる例です;)
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ループが強制終了されます。
他のほとんどの人と同じように、私は有限回数試行し、試行間で寝ることをお勧めします。この方法では、リモートサーバーに実際に何かが発生した場合に無限ループに陥ることはありません。
また、期待する特定の例外が発生した場合にのみ続行することをお勧めします。これにより、予期しない例外を処理できます。
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以外の例外が発生するまで、ループの残りをスキップします。
エラーのために再試行するときは、常に次のことを行う必要があります。
これらの懸念をカバーしながらこの問題を解決する簡単な一般的な方法は、 backoff ライブラリを使用することです。基本的な例:
import backoff
@backoff.on_exception(
backoff.expo,
MyException,
max_tries=5
)
def make_request(self, data):
# do the request
このコードは、再試行ロジックを実装するデコレーターでmake_requestをラップします。特定のエラーMyException
が発生するたびに、再試行を5回に制限して再試行します。 指数バックオフ は、このコンテキストでは、リモートサーバーでの再試行による追加の負荷を最小限に抑えるのに役立ちます。
以下は、エラーを文字列としてキャプチャするために使用する短いコードです。成功するまで再試行します。これはすべての例外をキャッチしますが、必要に応じて変更できます。
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では、ループを早く終了するか、再試行の間にスリープする必要があります。