web-dev-qa-db-fra.com

Comment réessayer après exception?

J'ai une boucle commençant par for i in range(0, 100). Normalement, il fonctionne correctement, mais parfois, il échoue en raison des conditions du réseau. Actuellement, je l'ai configuré pour qu'en cas d'échec, il sera continue dans la clause except (passez au numéro suivant pour i).

Est-il possible pour moi de réaffecter le même numéro à i et de réexécuter l'itération échouée de la boucle?

207
FurtiveFelon

Faites un while True à l'intérieur de votre boucle for, mettez votre code try à l'intérieur et coupez cette boucle while uniquement lorsque votre code réussit.

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

Je préfère limiter le nombre de tentatives, de sorte que s'il y a un problème avec cet élément spécifique, vous passerez éventuellement au suivant, ainsi:

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.
156
xorsyst

Le nouvelle tentative de package est un bon moyen de réessayer un bloc de code en cas d'échec.

Par exemple:

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

Voici une solution similaire à d'autres solutions, mais elle lève l'exception si elle ne réussit pas dans le nombre prescrit ou si elle réessaye.

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
17
TheHerk

L'approche la plus "fonctionnelle" sans utiliser ces vilaines boucles while:

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

tryAgain()
15
restbeckett

Le moyen le plus clair serait de définir explicitement i. Par exemple:

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

    except MyException:
        continue
9
Tomi Kyöstilä

Utiliser la récursion

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

Une solution générique avec un délai d'attente:

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)

Usage:

for retry in onerror_retry(SomeSpecificException, do_stuff):
    retry()
5
Laurent LAPORTE

Il y a quelque chose de similaire dans = Python Decorator Library .

N'oubliez pas qu'il ne teste pas les exceptions, mais la valeur de retour. Il réessaie jusqu'à ce que la fonction décorée retourne True.

Une version légèrement modifiée devrait faire l'affaire.

4
Michael
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

Ma version est similaire à plusieurs des précédentes, mais n'utilise pas de boucle while distincte et lève à nouveau la dernière exception si toutes les tentatives échouent. Peut définir explicitement err = None en haut, mais pas strictement car il ne doit exécuter que le dernier bloc else en cas d'erreur et donc err est défini.

3
n8henrie

Utilisation de while et d'un compteur:

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
2
Ranju R

Vous pouvez utiliser le package Python retrying. Réessayer

Il est écrit en Python pour simplifier la tâche consistant à ajouter un comportement de nouvelle tentative à peu près n'importe quoi.

2
ManJan

Si vous voulez une solution sans boucles imbriquées et appelant break en cas de succès, vous pouvez développer rapidement et rapidement retriable pour tout élément itératif. Voici un exemple de problème de réseau que je rencontre souvent - l'authentification enregistrée expire. Son utilisation se lirait comme ceci:

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
Mikhail

J'utilise ce qui suit dans mes codes,

   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
H S Rathore