2011-01-05 4 views
32

Ich habe ein Stück Code in Python, die probabilistisch einen Fehler verursacht, weil es auf einen Server zugreifen und manchmal hat dieser Server einen 500 internen Serverfehler. Ich möchte es weiter versuchen, bis ich den Fehler nicht bekomme. Meine Lösung war:In Python versuchen, bis kein Fehler

while True: 
    try: 
     #code with possible error 
    except: 
     continue 
    else: 
     #the rest of the code 
     break 

Das scheint wie ein Hack für mich. Gibt es einen pythonischen Weg, dies zu tun?

+4

Err ... was passiert, wenn der Remote-Server stirbt? Wird dies dort Platz nehmen und 100% eines CPU-Kerns verbrauchen? – user9876

+0

Fortsetzung sollte in sonst sein und einbrechen außer. Ist es ein Tippfehler? –

+2

@aand: Nein. Wenn eine Ausnahme auftritt, möchte er es erneut versuchen (lies: 'continue'), aber wenn keine Ausnahme auftritt, möchte er einige Dinge (skizziert durch einen Kommentar) und aus diesem seltsamen Missbrauch von eine Schleife. ('else' wird ausgeführt, wenn keine Ausnahme auftritt, ist das das fehlende Stück?) – delnan

Antwort

33

Es wird nicht viel sauberer bekommen. Das ist keine sehr saubere Sache. Bestenfalls (was ohnehin besser lesbar wäre, da die Bedingung für die break dort oben mit der while steht), könnte man eine Variable result = None anlegen und währenddessen is None schleifen. Sie sollten auch die Variablen anpassen und Sie können continue durch die semantisch vielleicht korrekte pass ersetzen (Sie kümmern sich nicht, wenn ein Fehler auftritt, Sie wollen es einfach ignorieren) und die break löschen - das bekommt auch den Rest des Codes, der wird nur einmal außerhalb der Schleife ausgeführt. Beachten Sie auch, dass bare except: Klauseln aus Gründen given in the documentation böse sind.

Beispiel alle oben mit eingebautem

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 
+3

Wenn es einen anhaltenden Grund gibt, dass die Verbindung nicht erfolgreich ist, wird diese Lösung in einer Endlosschleife schleifen. –

+3

@BradKoch Natürlich. Das ist der Frage inhärent, und außerdem ist jede Behebung (wie etwa ein Gesamt-Timeout oder eine begrenzte Anzahl von Versuchen) relativ orthogonal zu den von mir beschriebenen Änderungen. – delnan

+2

Aber jede vorgeschlagene Antwort sollte sicher sein, oder zumindest die Fallstricke beachten. Dies bietet keinen Schutz vor 100% CPU-Verbrauch und gefährdet zukünftige Leser. –

15

Vielleicht so etwas wie folgt aus:

connected = False 

while not connected: 
    try: 
     try_connect() 
     connected = True 
    except ...: 
     pass 
+3

Jede vorgeschlagene Antwort sollte sicher sein, oder bei Beachten Sie die Fallstricke. Dies bietet keinen Schutz vor 100% CPU-Verbrauch und gefährdet zukünftige Leser. –

1

Vielleicht Dekorateur basiert? Sie können die Liste der Ausnahmen, für die wir einen Wiederholungsversuch durchführen möchten, und/oder die Anzahl der Versuche als Liste der Dekoratoren übergeben.

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 die ‚versuchen‘ Teil zu einem anderen funcion bewegt werden sollte becouse wir es in beiden Schleifen verwenden, aber es ist nur Beispiel;)

+0

Bit eines späten Kommentars, aber die Verdoppelung des Codes könnte vermieden werden, indem Sie "für _ in itertools.repeat (keine, mal = Versuche):" Wenn tryes None ist, wird die Schleife für immer fortgesetzt, aber wenn tries ist eine Zahl , es endet danach viele Iterationen. –

0

Hier ist ein kurzes Stück Code, den ich verwenden, um die Fehler zu erfassen als eine Schnur. Wird versuchen, bis es gelingt. Dies fängt alle Ausnahmen ab, aber Sie können dies nach Belieben ä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 wird in einer Endlosschleife stecken, bis keine Ausnahme auftritt. Dies ist nur ein einfaches Beispiel und erfordert möglicherweise, dass Sie früher aus der Schleife ausbrechen oder zwischen Wiederholungen schlafen.

6

Hier ist eine, die hart nach 4 Versuchen fehlschlägt, und wartet 2 Sekunden zwischen den Versuchen. Ändern Sie, wie Sie wollen bekommen, was Sie bilden wollen diese:

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 
+1

Ich mag diese Antwort besser als andere, weil diese wegen der Schlaffunktion "schön" zu anderen Prozessen ist und auch nur begrenzte Versuche hat. –

1

Hier ist eine Utility-Funktion, die ich schrieb den Wiederholungs bis Erfolg in eine sauberere zu wickeln Paket. Es verwendet die gleiche Grundstruktur, verhindert aber Wiederholungen. Es könnte modifiziert werden, um die Ausnahme beim letzten Versuch relativ leicht zu fangen und erneut zu lösen.

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 wie dieses

result = try_until(my_function, 100, 1000) 

aufgerufen werden, wenn Sie Argumente an my_function übergeben müssen, können Sie entweder das tun, indem try_until uns darauf, die Argumente hat, oder indem sie in einem kein Argument Lambda-Verpackung :

result = try_until(lambda : my_function(x,y,z), 100, 1000) 
1

Die itertools.iter_except Rezepte kapselt diese Idee „eine Funktion wiederholt aufrufen, bis eine Ausnahme ausgelöst wird“. Es ist ähnlich wie die angenommene 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 sicher die letztere Code direkt umzusetzen. Für die Bequemlichkeit verwende ich eine separate Bibliothek, more_itertools, die dieses Rezept für uns implementiert (optional).

Beispiel:

import more_itertools as mit 

list(mit.iter_except([0, 1, 2].pop, IndexError)) 
# [2, 1, 0] 

Hier wird die pop Methode (oder gegebene Funktion) für jede Iteration des Listenobjekt, bis ein IndexError aufgerufen wird, wird erhöht.

Für Ihren Fall gegeben einig connect_function und erwartete Fehler, können Sie einen Iterator machen, die die Funktion wiederholt aufrufen, bis eine Ausnahme ausgelöst wird, zB:

mit.iter_except(connect_function, ConnectionError) 

An diesem Punkt behandeln sie wie jeden anderen Iterator indem Sie es überschleifen oder next() anrufen.

Verwandte Themen