2014-11-07 25 views
6

Qual è il modo più elegante di ripetere qualcosa dopo aver causato un'eccezione in python?Come ripetere qualcosa su eccezione in python?

Ho qualcosa di simile [pseudo codice come un esempio]:

try: 
    do_some_database_stuff() 
except DatabaseTimeoutException: 
    reconnect_to_database() 
    do_some_database_stuff() # just do it again 

Ma immaginate se non ho una bella funzione, ma un sacco di codice, invece. Il codice duplicato non è molto bello.

quindi penso che questo è leggermente migliore:

while True: 
    try: 
    do_some_database_stuff() 
    break 
    except DatabaseTimeoutException: 
    reconnect_to_database() 

che è abbastanza buono, se l'eccezione risolve davvero il problema. Se non ho bisogno di un contatore per evitare un ciclo indefinito:

i = 0 
while i < 5: 
    try: 
    do_some_database_stuff() 
    break 
    except DatabaseTimeoutException: 
    reconnect_to_database() 
    i += 1 

Ma poi non so davvero se ha funzionato in modo che sia anche:

while i <= 5: 
    try: 
    do_some_database_stuff() 
    break 
    except DatabaseTimeoutException: 
    if i != 5: 
    reconnect_to_database() 
    else: 
     raise DatabaseTimeoutException 
    i += 1 

Come si può vedere che inizia a diventare molto disordinato.

Qual è il modo più elegante di esprimere questa logica?

  • provare qualcosa
  • se non riesce applicare correzione
  • provare n più volte tra cui la correzione
  • se continua a fallire darmi un errore per evitare che un ciclo indefinito

risposta

6

È possibile utilizzare un ciclo "for-else":

for ii in range(5): 
    try: 
     do_some_database_stuff() 
     break 
    except DatabaseTimeoutException: 
     reconnect_to_database() 
else: 
    raise DatabaseTimeoutException 

Oppure, senza:

for ii in range(5): 
    try: 
     do_some_database_stuff() 
     break 
    except DatabaseTimeoutException: 
     if ii == 4: 
      raise 
     reconnect_to_database() 
+0

La risposta principale a queste domande è "Ma ogni volta che vedi [un per-else] costruisci, un'alternativa migliore è ...". Sono d'accordo. Meglio avvolgere il blocco di codice in una funzione e utilizzare il rendimento piuttosto che l'interruzione. – Dunes

+1

@Dunes: è per questo che ho fornito un'alternativa, per coloro che trovano un po 'troppo il contrario. Sono fuori discussione, personalmente, è certamente una questione di gusti. –

+1

La versione 'for-else' non è equivalente poiché eseguirà' reconnect_to_database' 5 volte su 5 errori invece di 4. – Veedrac

1

Personalmente non sono un fan del costrutto for else. Non penso sia intuitivo. La prima volta che l'ho letto ho pensato che significava "fai per loop (...), se iterable era vuoto allora ...".

È necessario inserire il codice in una funzione. Se do_some_database_stuff() viene completato correttamente, è possibile utilizzare l'istruzione return per tornare presto dalla funzione.

es.

def try_to_do_some_database_stuff(): 
    for i in range(num_times): 
     try: 
      return do_some_database_stuff() 
     except DatabaseTimeoutException: 
      reconnect_to_database() 
    raise DatabaseTimeoutException 

Se ti trovi a utilizzare questo costrutto molto, puoi renderlo più generico.

def try_to_do(func, catch, times=2, on_exception=None): 
    for i in range(times): 
     try: 
      return func() 
     except catch: 
      if on_exception: 
       on_exception() 
    raise catch 

try_to_do(do_some_database_stuff, catch=DatabaseTimeoutException, times=5, 
    on_exception=reconnect_to_database) 

È possibile utilizzare functools.partial se è necessario passare gli argomenti alle funzioni.

+0

Questo eseguirà 'reconnect_to_database' 5 volte per 5 errori che sembra un po 'inutile. – Veedrac

+0

Sta copiando il comportamento dei loop nella domanda. Un ulteriore 'i + 1 Dunes

Problemi correlati