2011-01-05 13 views
32

Ho un pezzo di codice in Python che sembra causare un errore in modo probabilistico perché sta accedendo a un server e talvolta il server ha un errore interno di 500 server. Voglio continuare a provare fino a quando non ottengo l'errore. La mia soluzione era:In Python provare fino a nessun errore

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

Questo mi sembra un trucco per me. C'è un modo più pitonico per farlo?

+4

Err ... cosa succede quando il server remoto muore? Sarà seduto lì consumando il 100% del core della CPU? – user9876

+0

continuare dovrebbe essere in altro e irrompere tranne. È un errore di battitura? –

+2

@aand: No. Se si verifica un'eccezione, vuole riprovare (leggi: 'continua'), ma se non si verifica alcuna eccezione, vuole fare alcune cose (delineato da un commento) e uscire da quello strano abuso di un ciclo. ('else' viene eseguito se non si verifica alcuna eccezione, è il pezzo mancante?) – delnan

risposta

33

non otterrà molto più pulito. Questa non è una cosa molto pulita da fare. Nella migliore delle ipotesi (che sarebbe comunque più leggibile, dal momento che le condizioni per lo) sono disponibili con lo), è possibile creare una variabile result = None e il ciclo mentre è is None. Dovresti anche regolare le variabili e puoi sostituire continue con semanticamente forse corretto pass (non ti interessa se si verifica un errore, devi solo ignorarlo) e rilasciare il break - questo ottiene anche il resto del codice, che Esegue solo una volta, fuori dal ciclo. Si noti inoltre che le clausole nulle except: sono malvagie per i motivi given in the documentation.

Esempio incorpora tutto quanto sopra:

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

Se esiste una ragione persistente per cui la connessione non riesce, questa soluzione verrà eliminata in un ciclo infinito. –

+3

@BradKoch Naturalmente. Ciò è inerente alla domanda e inoltre qualsiasi correzione (come un timeout complessivo o un numero limitato di tentativi) è relativamente ortogonale ai cambiamenti che descrivo. – delnan

+2

Ma qualsiasi risposta proposta dovrebbe essere sicura, o almeno notare le insidie. Questo non offre protezione contro il 100% del consumo di CPU e mette in pericolo i futuri lettori. –

15

Forse qualcosa di simile:

connected = False 

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

Qualsiasi risposta proposta deve essere sicura o almeno nota le insidie. Questo non offre protezione contro il 100% del consumo di CPU e mette in pericolo i futuri lettori. –

1

Forse basato decoratore? È possibile passare come elenco degli argomenti del decoratore di eccezioni su cui si desidera riprovare e/o numero di tentativi.

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() 

naturalmente la parte 'provare' dovrebbe essere spostato in un altro funcion siccome abbiamo utilizzarlo in entrambi i loop, ma è solo esempio;)

+0

Bit di un commento in ritardo, ma il raddoppio del codice potrebbe essere evitato usando "for _ in itertools.repeat (None, times = tries):" Se try è None, il ciclo continua per sempre, ma se try è un numero , termina dopo che molte iterazioni. –

0

Ecco un breve pezzo di codice che utilizzo per catturare l'errore come una stringa. Ritenterà finché non avrà successo. Questo cattura tutte le eccezioni ma puoi cambiarlo come desideri.

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 

ATTENZIONE: Questo codice verrà bloccato in un ciclo eterno finché non si verifica non fa eccezione. Questo è solo un semplice esempio e POTREBBE richiedere di uscire prima del ciclo o dormire tra i tentativi.

6

Ecco uno che non funziona correttamente dopo 4 tentativi e attende 2 secondi tra un tentativo e l'altro. Cambia come si desidera per ottenere ciò che si vuole formare questo:

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 

Ecco un esempio con 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

Mi piace questa risposta meglio di altri perché questo è "bello" per altri processi dovuti alla funzione sleep e ha anche tentativi limitati. –

1

Ecco una funzione di utilità che ho scritto per avvolgere la tentativi fino a quando il successo in un più ordinato pacchetto. Usa la stessa struttura di base, ma impedisce la ripetizione. Potrebbe essere modificato per catturare e rilanciare l'eccezione nel tentativo finale relativamente facilmente.

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' 

può quindi essere chiamato come questo

result = try_until(my_function, 100, 1000) 

Se è necessario passare gli argomenti a my_function, è possibile farlo avendo try_until avanti gli argomenti, o avvolgendolo in un lambda alcun argomento :

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

Le ricette itertools.iter_except racchiude l'idea di "chiamare una funzione più volte fino a quando viene generata un'eccezione". È simile alla risposta accettata, ma la ricetta fornisce invece un iteratore.

Dalle ricette:

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 

Si può certamente implementare direttamente il secondo codice. Per comodità, io uso una libreria separata, more_itertools, che implementa questa ricetta per noi (opzionale).

Esempio:

import more_itertools as mit 

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

Qui il pop metodo (o data funzione) viene chiamata per ogni iterazione dell'oggetto Lista finché un IndexError viene sollevata.

Per il vostro caso, dato un certo connect_function e di errore previsto, si può fare un iteratore che chiama ripetutamente la funzione fino a quando viene sollevata un'eccezione, ad esempio:

mit.iter_except(connect_function, ConnectionError) 

A questo punto, trattarlo come qualsiasi altro iteratore collegandoci sopra o chiamando next().

Problemi correlati