Ich habe einen Code in Python, der wahrscheinlich einen Fehler verursacht, weil er auf einen Server zugreift und dieser Server manchmal einen internen Serverfehler von 500 aufweist. Ich möchte es so lange versuchen, bis ich den Fehler nicht mehr bekomme. Meine Lösung war:
while True:
try:
#code with possible error
except:
continue
else:
#the rest of the code
break
Das scheint mir ein Hack zu sein. Gibt es einen pythonischeren Weg, dies zu tun?
python
error-handling
murgatroid99
quelle
quelle
continue
, aber wenn keine Ausnahme auftritt, möchte er einige Dinge (durch einen Kommentar skizziert) ausführen und aus diesem seltsamen Missbrauch einer Schleife herauskommen. (wirdelse
ausgeführt, wenn keine Ausnahme auftritt, ist das das fehlende Teil?)Antworten:
Es wird nicht viel sauberer. Dies ist keine sehr saubere Sache. Bestenfalls (was ohnehin besser lesbar wäre, da die Bedingung für das
break
oben mit dem istwhile
) könnten Sie eine Variable erstellenresult = None
und dabei eine Schleife ausführenis None
. Sie sollten auch die Variablen anpassen und sie durchcontinue
die semantisch möglicherweise korrekten ersetzenpass
(es ist Ihnen egal, ob ein Fehler auftritt, Sie möchten ihn nur ignorieren) und die löschenbreak
- dies erhält auch den Rest des Codes, der nur einmal ausgeführt wird aus der Schleife. Beachten Sie auch, dass bloßeexcept:
Klauseln aus den in der Dokumentation angegebenen Gründen böse sind .Beispiel mit allen oben genannten:
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
quelle
Hier ist eine, die nach 4 Versuchen schwer fehlschlägt und zwischen den Versuchen 2 Sekunden wartet. Ändern Sie, wie Sie möchten, was Sie von diesem wollen:
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
Hier ist ein Beispiel mit Backoff:
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
quelle
Vielleicht so etwas:
connected = False while not connected: try: try_connect() connected = True except ...: pass
quelle
Das
itertools.iter_except
Rezept fasst diese Idee zusammen, "eine Funktion wiederholt aufzurufen, bis eine Ausnahme ausgelöst wird". Es ähnelt der akzeptierten Antwort, aber das Rezept gibt stattdessen einen Iterator.Aus den Rezepten:
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
Sie können den letzteren Code sicherlich direkt implementieren. Der Einfachheit halber verwende ich eine separate Bibliothek,
more_itertools
die dieses Rezept für uns implementiert (optional).Code
import more_itertools as mit list(mit.iter_except([0, 1, 2].pop, IndexError)) # [2, 1, 0]
Einzelheiten
Hier wird die
pop
Methode (oder gegebene Funktion) für jede Iteration des Listenobjekts aufgerufen, bis einIndexError
ausgelöst wird.In Ihrem Fall können Sie bei einigen
connect_function
und erwarteten Fehlern einen Iterator erstellen, der die Funktion wiederholt aufruft, bis eine Ausnahme ausgelöst wird, zBehandeln Sie es an dieser Stelle wie jeden anderen Iterator, indem Sie es durchlaufen oder aufrufen
next()
.quelle
Wenn Sie es aufgrund eines Fehlers erneut versuchen, sollten Sie immer:
Eine einfache generische Möglichkeit, dieses Problem zu lösen und gleichzeitig diese Probleme zu lösen, ist die Verwendung der Backoff- Bibliothek. Ein einfaches Beispiel:
import backoff @backoff.on_exception( backoff.expo, MyException, max_tries=5 ) def make_request(self, data): # do the request
Dieser Code umschließt make_request mit einem Dekorator, der die Wiederholungslogik implementiert. Wir versuchen es immer wieder, wenn unser spezifischer Fehler
MyException
auftritt, mit einem Limit von 5 Wiederholungsversuchen. Exponentielles Backoff ist in diesem Zusammenhang eine gute Idee, um die zusätzliche Belastung zu minimieren, die unsere Wiederholungsversuche für den Remote-Server bedeuten.quelle
Hier ist eine Dienstprogrammfunktion, die ich geschrieben habe, um den Wiederholungsversuch bis zum Erfolg in ein übersichtlicheres Paket zu packen. Es verwendet dieselbe Grundstruktur, verhindert jedoch Wiederholungen. Es könnte modifiziert werden, um die Ausnahme beim letzten Versuch relativ leicht zu fangen und erneut zu werfen.
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'
Kann dann so genannt werden
result = try_until(my_function, 100, 1000)
Wenn Sie Argumente übergeben müssen,
my_function
können Sie dies entweder tun, indem Sietry_until
die Argumente weiterleiten, oder indem Sie sie in ein Lambda ohne Argument einschließen:result = try_until(lambda : my_function(x,y,z), 100, 1000)
quelle
Vielleicht Dekorateur basiert? Sie können als Dekorationsargumente eine Liste der Ausnahmen übergeben, für die wir es erneut versuchen möchten, und / oder die Anzahl der Versuche.
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()
Natürlich sollte der 'try'-Teil auf eine andere Funktion verschoben werden, da wir ihn in beiden Schleifen verwenden, aber es ist nur ein Beispiel;)
quelle
Wie die meisten anderen würde ich empfehlen, eine begrenzte Anzahl von Malen zu versuchen und zwischen den Versuchen zu schlafen. Auf diese Weise befinden Sie sich nicht in einer Endlosschleife, falls dem Remote-Server tatsächlich etwas zustoßen sollte.
Ich würde auch empfehlen, nur fortzufahren, wenn Sie die spezifische Ausnahme erhalten, die Sie erwarten. Auf diese Weise können Sie weiterhin Ausnahmen behandeln, die Sie möglicherweise nicht erwarten.
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
Außerdem benötigen Sie keinen else-Block. Aufgrund der Fortsetzung im Ausnahmeblock überspringen Sie den Rest der Schleife, bis der try-Block funktioniert, die while-Bedingung erfüllt ist oder eine andere Ausnahme als HTTPError auftritt.
quelle
Was ist mit der Wiederholungsbibliothek auf Pypi ? Ich benutze es seit einer Weile und es macht genau das, was ich will und mehr (bei Fehler erneut versuchen, bei Keine erneut versuchen, bei Zeitüberschreitung erneut versuchen). Unten ist ein Beispiel von ihrer Website:
import random from retrying import retry @retry def do_something_unreliable(): if random.randint(0, 10) > 1: raise IOError("Broken sauce, everything is hosed!!!111one") else: return "Awesome sauce!" print do_something_unreliable()
quelle
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)
Das sollte funktionieren. Es setzt e auf '' und die while-Schleife prüft, ob es noch '' ist. Wenn bei der try-Anweisung ein Fehler festgestellt wird, wird gedruckt, dass die Verbindung abgelehnt wurde, 1 Sekunde gewartet und dann von vorne begonnen. Es wird so lange fortgesetzt, bis beim Versuch kein Fehler mehr auftritt. Dadurch wird e auf '' gesetzt, wodurch die while-Schleife beendet wird.
quelle
Hier ist ein kurzer Code, den ich verwende, um den Fehler als Zeichenfolge zu erfassen. Wird es erneut versuchen, bis es erfolgreich ist. Dies fängt alle Ausnahmen ab, aber Sie können dies nach Ihren Wünschen ändern.
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
WARNUNG: Dieser Code bleibt in einer Forever-Schleife stecken, bis keine Ausnahme mehr auftritt. Dies ist nur ein einfaches Beispiel und möglicherweise müssen Sie früher aus der Schleife ausbrechen oder zwischen den Wiederholungsversuchen schlafen.
quelle
Ich versuche das jetzt, das habe ich mir ausgedacht;
placeholder = 1 while placeholder is not None: try: #Code placeholder = None except Exception as e: print(str(datetime.time(datetime.now()))[:8] + str(e)) #To log the errors placeholder = e time.sleep(0.5) continue
quelle