で始まるループがありますfor i in range(0, 100)
。通常は正常に動作しますが、ネットワークの状態が原因で失敗する場合もあります。現在私はそれが失敗したときにそれがcontinue
except節にあるように設定しています(の次の番号に続きi
ます)。
同じ番号を再度割り当てi
、失敗したループの繰り返しをもう一度実行することは可能ですか?
で始まるループがありますfor i in range(0, 100)
。通常は正常に動作しますが、ネットワークの状態が原因で失敗する場合もあります。現在私はそれが失敗したときにそれがcontinue
except節にあるように設定しています(の次の番号に続きi
ます)。
同じ番号を再度割り当てi
、失敗したループの繰り返しをもう一度実行することは可能ですか?
回答:
くださいwhile True
あなたのforループの内側には、あなたの置くtry
ことから、コードの内部、および休憩をwhile
あなたのコードが成功した場合にのみ、ループ。
for i in range(0,100):
while True:
try:
# do stuff
except SomeSpecificException:
continue
break
continue
リトライwhile
ループは、もちろん、ないfor
、そう(!)i
されていない「次の」何でも-それはそれは同じの前の(失敗した)足にあったとまったく同じだwhile
もちろん、。
私は再試行の回数を制限することを好むので、その特定のアイテムに問題がある場合は、最終的に次のアイテムに進むため、次のようになります。
for i in range(100):
for attempt in range(10):
try:
# do thing
except:
# perhaps reconnect, etc.
else:
break
else:
# we failed all the attempts - deal with the consequences.
else:
節を完全に使用しますfor
。
else
節は、try
あなたが探しているこの「成功した場合、それから壊れる」ことを行います。
except
再試行パッケージには、失敗した場合に、コードのブロックを再試行するための良い方法です。
例えば:
@retry(wait_random_min=1000, wait_random_max=2000)
def wait_random_1_to_2_s():
print("Randomly wait 1 to 2 seconds between retries")
これは他と同様の解決策ですが、規定の回数または再試行で成功しない場合は例外が発生します。
tries = 3
for i in range(tries):
try:
do_the_thing()
except KeyError as e:
if i < tries - 1: # i is zero indexed
continue
else:
raise
break
retries
は誤解を招くものです。それはむしろそうあるべきtries
です。
これらの醜いwhileループを使用しない、より「機能的な」アプローチ:
def tryAgain(retries=0):
if retries > 10: return
try:
# Do stuff
except:
retries+=1
tryAgain(retries)
tryAgain()
except: tryAgain(retries+1)
最も明確な方法は、明示的に設定することi
です。例えば:
i = 0
while i < 100:
i += 1
try:
# do stuff
except MyException:
continue
i += 1
直後に置くといいかもしれません# do stuff
。
range
この種のものに使用する必要があります。
タイムアウトのある一般的なソリューション:
import time
def onerror_retry(exception, callback, timeout=2, timedelta=.1):
end_time = time.time() + timeout
while True:
try:
yield callback()
break
except exception:
if time.time() > end_time:
raise
elif timedelta > 0:
time.sleep(timedelta)
使用法:
for retry in onerror_retry(SomeSpecificException, do_stuff):
retry()
except exception:
try … except
を使用できますif
。しかし、それはそれほどPythonicではありません。
callback
があります。関数が呼び出されることはありません。括弧を忘れたので、に置き換えてくださいcallback()
。
for _ in range(5):
try:
# replace this with something that may fail
raise ValueError("foo")
# replace Exception with a more specific exception
except Exception as e:
err = e
continue
# no exception, continue remainder of code
else:
break
# did not break the for loop, therefore all attempts
# raised an exception
else:
raise err
私のバージョンは上記のいくつかに似ていますが、個別のwhile
ループを使用せず、すべての再試行が失敗した場合に最新の例外を再発生させます。err = None
先頭に明示的に設定できelse
ますが、エラーが発生してerr
設定された場合にのみ最終ブロックを実行するため、厳密には必要ありません。
Pythonデコレータライブラリにも同様のものがあります。ます。
例外をテストするのではなく、戻り値をテストすることに注意してください。デコレートされた関数がTrueを返すまで再試行します。
少し変更したバージョンでうまくいくはずです。
retrying
:tenacity
およびbackoff
(2020更新)再試行のライブラリは、以前に移動するための方法だったが、悲しいことに、それはいくつかのバグを持っており、2016年の他の選択肢があるように見えるので、それはすべての更新プログラムを持っていないバックオフと粘り強さ。これを書いている間、執念にはもっと多くのGItHubスター(2.3k対1.2k)があり、最近更新されたため、それを使用することにしました。次に例を示します。
from functools import partial
import random # producing random errors for this example
from tenacity import retry, stop_after_delay, wait_fixed, retry_if_exception_type
# Custom error type for this example
class CommunicationError(Exception):
pass
# Define shorthand decorator for the used settings.
retry_on_communication_error = partial(
retry,
stop=stop_after_delay(10), # max. 10 seconds wait.
wait=wait_fixed(0.4), # wait 400ms
retry=retry_if_exception_type(CommunicationError),
)()
@retry_on_communication_error
def do_something_unreliable(i):
if random.randint(1, 5) == 3:
print('Run#', i, 'Error occured. Retrying.')
raise CommunicationError()
上記のコードは次のようなものを出力します:
Run# 3 Error occured. Retrying.
Run# 5 Error occured. Retrying.
Run# 6 Error occured. Retrying.
Run# 6 Error occured. Retrying.
Run# 10 Error occured. Retrying.
.
.
.
のその他の設定は、粘り強さのGitHubページにtenacity.retry
記載されています。
ネストされたループがなくbreak
、成功時に呼び出すソリューションが必要な場合retriable
は、イテラブルのクイックラップを開発できます。これは私がよく遭遇するネットワークの問題の例です-保存された認証が期限切れになります。これを使用すると、次のようになります。
client = get_client()
smart_loop = retriable(list_of_values):
for value in smart_loop:
try:
client.do_something_with(value)
except ClientAuthExpired:
client = get_client()
smart_loop.retry()
continue
except NetworkTimeout:
smart_loop.retry()
continue
これがこの問題に対する私の見解です。次のretry
関数は、次の機能をサポートしています。
import time
def retry(func, ex_type=Exception, limit=0, wait_ms=100, wait_increase_ratio=2, logger=None):
attempt = 1
while True:
try:
return func()
except Exception as ex:
if not isinstance(ex, ex_type):
raise ex
if 0 < limit <= attempt:
if logger:
logger.warning("no more attempts")
raise ex
if logger:
logger.error("failed execution attempt #%d", attempt, exc_info=ex)
attempt += 1
if logger:
logger.info("waiting %d ms before attempt #%d", wait_ms, attempt)
time.sleep(wait_ms / 1000)
wait_ms *= wait_increase_ratio
使用法:
def fail_randomly():
y = random.randint(0, 10)
if y < 10:
y = 0
return x / y
logger = logging.getLogger()
logger.setLevel(logging.INFO)
logger.addHandler(logging.StreamHandler(stream=sys.stdout))
logger.info("starting")
result = retry.retry(fail_randomly, ex_type=ZeroDivisionError, limit=20, logger=logger)
logger.info("result is: %s", result)
詳細については、私の投稿を参照してください。
これを修正する方法についての私の考えは次のとおりです。
j = 19
def calc(y):
global j
try:
j = j + 8 - y
x = int(y/j) # this will eventually raise DIV/0 when j=0
print("i = ", str(y), " j = ", str(j), " x = ", str(x))
except:
j = j + 1 # when the exception happens, increment "j" and retry
calc(y)
for i in range(50):
calc(i)
私は最近、この問題の解決策についてpythonを使用しました。stackoverflowの訪問者と共有できて嬉しいです。必要に応じてフィードバックをお寄せください。
print("\nmonthly salary per day and year converter".title())
print('==' * 25)
def income_counter(day, salary, month):
global result2, result, is_ready, result3
result = salary / month
result2 = result * day
result3 = salary * 12
is_ready = True
return result, result2, result3, is_ready
i = 0
for i in range(5):
try:
month = int(input("\ntotal days of the current month: "))
salary = int(input("total salary per month: "))
day = int(input("Total Days to calculate> "))
income_counter(day=day, salary=salary, month=month)
if is_ready:
print(f'Your Salary per one day is: {round(result)}')
print(f'your income in {day} days will be: {round(result2)}')
print(f'your total income in one year will be: {round(result3)}')
break
else:
continue
except ZeroDivisionError:
is_ready = False
i += 1
print("a month does'nt have 0 days, please try again")
print(f'total chances left: {5 - i}')
except ValueError:
is_ready = False
i += 1
print("Invalid value, please type a number")
print(f'total chances left: {5 - i}')
try句が成功した場合にのみループ変数をインクリメントします
range(100)
最初のパラメーターなしで使用できます。Python 2.xを使用している場合、を使用することもできますxrange(100)
。これにより、イテレータが生成され、使用するメモリが少なくなります。(100個のオブジェクトだけで問題になるわけではありません。)