例外後に再試行する方法は?


252

で始まるループがありますfor i in range(0, 100)。通常は正常に動作しますが、ネットワークの状態が原因で失敗する場合もあります。現在私はそれが失敗したときにそれがcontinueexcept節にあるように設定しています(の次の番号に続きiます)。

同じ番号を再度割り当てi、失敗したループの繰り返しをもう一度実行することは可能ですか?


1
range(100)最初のパラメーターなしで使用できます。Python 2.xを使用している場合、を使用することもできますxrange(100)。これにより、イテレータが生成され、使用するメモリが少なくなります。(100個のオブジェクトだけで問題になるわけではありません。)
GeorgSchölly10年


2
そのスレッドで任意の
例外

回答:


379

くださいwhile Trueあなたのforループの内側には、あなたの置くtryことから、コードの内部、および休憩をwhileあなたのコードが成功した場合にのみ、ループ。

for i in range(0,100):
    while True:
        try:
            # do stuff
        except SomeSpecificException:
            continue
        break

30
@イグナシオ、ハァッcontinueリトライwhileループは、もちろん、ないfor、そう(!)iされていない「次の」何でも-それはそれは同じの前の(失敗した)足にあったとまったく同じだwhileもちろん、。
Alex Martelli、2010年

13
xorsystが指摘しているように、再試行制限を設定することをお勧めします。そうしないと、かなり長い間ループが止まってしまう可能性があります。
Brad Koch

2
これは優れた例です。medium.com
Tony Melony 14

7
私は間違いなくwhile True:行を省きます。
1

1
@Sankalp、この回答は質問文にふさわしいようです。
zneak

189

私は再試行の回数を制限することを好むので、その特定のアイテムに問題がある場合は、最終的に次のアイテムに進むため、次のようになります。

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.

3
@ g33kz0r forループが壊れない場合、Pythonのfor-else構文はelse句を実行します。したがって、この場合、10回すべて試行して常に例外が発生すると、そのセクションが実行されます。
xorsyst 2015年

7
これは素晴らしい答えです!本当にもっと多くの賛成投票に値します。Pythonのすべての機能、特に、あまり知られていないのelse:節を完全に使用しますfor
pepoluan 2015

2
トライの最後に休憩は必要ありませんか?try:の追加の中断により、プロセスが正常に完了するとループが中断し、正常に完了しない場合は例外部分に直接進みます。それは理にかなっていますか?トライの最後に休憩を入れない場合は、100回実行するだけです。
トリスタン

1
@Tristan-のelse節は、tryあなたが探しているこの「成功した場合、それから壊れる」ことを行います。
PaulMcG 2016年

1
また、再試行にはforループを使用します。このコードのしわは、試行をexcept
中止した

69

再試行パッケージには、失敗した場合に、コードのブロックを再試行するための良い方法です。

例えば:

@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")

4
:より一般的には、は、PyPIは、再試行デコレーターのための複数のパッケージがある pypi.python.org/...
KERT

とにかく、失敗するたびに再試行の回数を出力できますか?
dim_user 2018年

8
私が理解していないように、よりアクティブなフォークはgithub.com/jd/tenacityであり、おそらくgithub.com/litl/backoffも使用できます。
Alexey Shrub

23

これは他と同様の解決策ですが、規定の回数または再試行で成功しない場合は例外が発生します。

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です。
Lukas

真の@ルーカス。修繕。
TheHerk

非常に良い解決策をありがとうございます。各試行の間に遅延を追加することで改善できます。APIを扱うときに非常に役立ちます。
2017

14

これらの醜いwhileループを使用しない、より「機能的な」アプローチ:

def tryAgain(retries=0):
    if retries > 10: return
    try:
        # Do stuff
    except:
        retries+=1
        tryAgain(retries)

tryAgain()

13
申し訳ありませんが、「醜いwhileループ」よりもずっと醜いようです。そして私は関数型プログラミングが好きです...
lvella

9
ただし、深く再帰しないようにする必要があります。Pythonのデフォルトのスタックサイズは1000です
Cal Paterson

5
これは「機能」になるだろうされている場合は、再帰は次のようになりますexcept: tryAgain(retries+1)
quamrana

この問題は、エラーを変数として渡す必要があることです。
lowzhao

11

最も明確な方法は、明示的に設定することiです。例えば:

i = 0
while i < 100:
    i += 1
    try:
        # do stuff

    except MyException:
        continue

37
それはCまたはC ++ですか?わかりません。
GeorgSchölly、2010年

5
@Georg質問で述べたように、これはPythonです。それとも、何らかの理由で皮肉っぽいところは?
Jakob Borg

2
これは、OPが要求したことを行いません。i += 1直後に置くといいかもしれません# do stuff
fmalina 2013年

5
Pythonicではありません。rangeこの種のものに使用する必要があります。
ミスティック14

2
これは間違いなく範囲を使用するはずです。
user2662833 2016年

5

タイムアウトのある一般的なソリューション:

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:
Pratik Khadloya

の代わりにステートメントtry … exceptを使用できますif。しかし、それはそれほどPythonicではありません。
Laurent LAPORTE

このソリューションは機能しません。trinket.io/python/caeead4f6b do_stuffによってスローされた例外は、ジェネレーターにバブリングしません。とにかく、なぜでしょうか?do_stuffはforループの本体で呼び出されます。これは、ジェネレーターにネストされていない、それ自体が外部レベルにあります。
isarandi

あなたの権利ですが、別の理由callbackがあります。関数が呼び出されることはありません。括弧を忘れたので、に置き換えてくださいcallback()
Laurent LAPORTE

5
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設定された場合にのみ最終ブロックを実行するため、厳密には必要ありません。



4

whileとcounterを使用する:

count = 1
while count <= 3:  # try 3 times
    try:
        # do_the_logic()
        break
    except SomeSpecificException as e:
        # If trying 3rd time and still error?? 
        # Just throw the error- we don't have anything to hide :)
        if count == 3:
            raise
        count += 1

4

再帰を使用する

for i in range(100):
    def do():
        try:
            ## Network related scripts
        except SpecificException as ex:
            do()
    do() ## invoke do() whenever required inside this loop

1
終了条件?または、これは100 *無限を実行しますか?
ingyhere

3

Python再試行パッケージを使用できます。 再試行

Pythonで記述されているため、再試行動作をほとんどすべてに追加するタスクが簡単になります。


2

の代替retryingtenacityおよび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記載されています


1

ネストされたループがなく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

1

私は私のコードで以下を使用します、

   for i in range(0, 10):
    try:
        #things I need to do
    except ValueError:
        print("Try #{} failed with ValueError: Sleeping for 2 secs before next try:".format(i))
        time.sleep(2)
        continue
    break


0

これがこの問題に対する私の見解です。次のretry関数は、次の機能をサポートしています。

  • 成功した場合、呼び出された関数の値を返します
  • 試行が終了した場合、呼び出された関数の例外を発生させます
  • 試行回数の制限(無制限の場合は0)
  • 試行間で待機(線形または指数)
  • 例外が特定の例外タイプのインスタンスである場合にのみ再試行します。
  • 試行のオプションのロギング
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)

詳細については、私の投稿を参照してください。


-2

これを修正する方法についての私の考えは次のとおりです。

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)

7
これは根本から外れています。
クリスジョンソン

-2

私は最近、この問題の解決策について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}')

-9

try句が成功した場合にのみループ変数をインクリメントします

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.