2009-10-06 4 views
59

Ho una comprensione di lista in Python in cui ogni iterazione può generare un'eccezione.Come posso gestire le eccezioni in una list comprehension in Python?

Per esempio, se ho:

eggs = (1,3,0,3,2) 

[1/egg for egg in eggs] 

Prendo un'eccezione ZeroDivisionError nel 3 ° elemento.

Come posso gestire questa eccezione e continuare l'esecuzione della comprensione della lista?

L'unico modo che posso pensare è quello di utilizzare una funzione di supporto:

def spam(egg): 
    try: 
     return 1/egg 
    except ZeroDivisionError: 
     # handle division by zero error 
     # leave empty for now 
     pass 

Ma questo sembra un po 'ingombrante per me.

C'è un modo migliore per farlo in Python?

Nota: Questo è un semplice esempio (vedi "per esempio" di cui sopra), che riuscii perché il mio esempio reale richiede un po 'di contesto. Non mi interessa evitare gli errori di divisione per zero, ma nel gestire le eccezioni nella comprensione di una lista.

+2

C'è un [PEP 463] (https://www.python.org/dev/peps/pep-0463/) per aggiungere un'espressione per gestire le eccezioni. Nel tuo esempio sarebbe '[1/egg except ZeroDivisionError: None for egg in (1,3,0,3,2)]'. Ma è ancora in modalità bozza. Il mio istinto è che non sarà accettato. Le espressioni Imho possono diventare troppo disordinate (controllare più eccezioni, avere combinazioni più complesse (più operatori logici, complesse comprensioni, ecc.) – cfi

+1

Si noti che per questo * specifico * esempio, si potrebbe usare un 'ndarray' numpy con le impostazioni appropriate in 'np. seterr' .Questo risulterebbe in '1/0 = nan' .Ma mi rendo conto che non generalizza ad altre situazioni in cui si presenta questa esigenza – gerrit

risposta

55

Non c'è espressione incorporata in Python che ti permette di ignorare un'eccezione (o restituire valori alternativi & c in caso di eccezioni), quindi è impossibile, letteralmente parlando, "gestire le eccezioni in una comprensione di lista" perché una comprensione di lista è un'espressione che contiene altre espressione, niente di più (cioè, no dichiarazioni, e solo le istruzioni possono catturare/ignorare/gestire eccezioni).

Le chiamate di funzione sono espressione e i corpi di funzione possono includere tutte le istruzioni desiderate, quindi delegare la valutazione della sotto-espressione soggetta a eccezioni a una funzione, come hai notato, è una soluzione possibile (altri, quando possibile, sono i controlli sui valori che potrebbero provocare eccezioni, come suggerito anche in altre risposte).

Le risposte corrette alla domanda "come gestire le eccezioni in una lista di comprensione" stanno tutte esprimendo parte di tutta questa verità: 1) letteralmente, cioè lessicamente nella comprensione stessa, non è possibile; 2) in pratica, deleghi il lavoro a una funzione o controlli i valori soggetti a errori quando ciò è fattibile. La tua ripetuta affermazione che questa non è una risposta è quindi infondata.

+6

Vedo. Quindi una risposta completa sarebbe che dovrei: 1. usare una funzione 2. non usare la list comprehension 3. cercare di prevenire l'eccezione piuttosto che gestirla. –

+8

Non vedo "non usare le list comprehensions" come parte della risposta a "come gestire le eccezioni nella comprensione delle liste", ma immagino che potresti ragionevolmente vederlo come una possibile conseguenza di "_eticamente IN_ the LC, non è possibile per gestire le eccezioni ", che è davvero la prima parte della risposta letterale. –

+0

Riesci a rilevare un errore nell'espressione di un generatore o nella comprensione del generatore? – Steve

18

È possibile utilizzare

[1/egg for egg in eggs if egg != 0] 

questo sarà semplicemente saltare elementi che sono pari a zero.

+10

Questo non risponde alla domanda su come gestire le eccezioni in una comprensione di lista –

+4

umm, sì, lo fa, evita la necessità di gestire le eccezioni Sì, non è sempre la soluzione giusta, ma è una cosa comune – Peter

+1

Capisco, riprendo il commento (anche se non lo eliminerò dal momento che la breve "discussione" migliora sulla risposta) –

8

No, non c'è un modo migliore. In molti casi è possibile utilizzare l'evasione come Pietro fa

L'altra opzione è di non usare comprensioni

eggs = (1,3,0,3,2) 

result=[] 
for egg in eggs: 
    try: 
     result.append(egg/0) 
    except ZeroDivisionError: 
     # handle division by zero error 
     # leave empty for now 
     pass 

Fino a voi decidere se questo è più ingombrante o no

+0

Come utilizzerei la comprensione qui? –

+0

@Nathan: non lo faresti. gnibbler dice: * No non c'è un modo migliore * – SilentGhost

+0

Scusa ... Ho mancato il 'non' nella sua risposta :-) –

58

mi rendo conto questa domanda è abbastanza vecchio, ma è anche possibile creare una funzione generale di fare questo genere di cose più facili:

def catch(func, handle=lambda e : e, *args, **kwargs): 
    try: 
     return func(*args, **kwargs) 
    except Exception as e: 
     return handle(e) 

Poi, nella vostra comprensione:

eggs = (1,3,0,3,2) 
[catch(lambda : 1/egg) for egg in eggs] 
[1, 0, ('integer division or modulo by zero'), 0, 0] 

Puoi Ovviamente rendi la funzione di default come vuoi (ad esempio, preferisci restituire 'None' per impostazione predefinita).

Spero che questo aiuti voi o eventuali futuri spettatori di questa domanda!

Nota: in python 3, avrei impostato solo la parola chiave argomento "handle" e l'avrei inserita alla fine dell'elenco degli argomenti. Ciò renderebbe molto più naturali le argomentazioni passeggere e simili.

+1

estremamente utile, grazie. Mentre sono d'accordo con i commenti teorici, questo mostra un approccio pratico alla risoluzione di un problema che ho avuto ripetutamente. – Paul

+1

Risposta completa. Una mod che suggerirei è di passare 'args' e' kwargs' fino a gestire pure. In questo modo puoi restituire "egg" invece di un '0' hardcoded, o un'eccezione come stai facendo. –

+1

Si potrebbe anche volere che il tipo di eccezione sia un argomento facoltativo (possono essere parametrizzati i tipi di eccezione?), In modo che le eccezioni impreviste vengano generate verso l'alto anziché ignorare tutte le eccezioni. – 00prometheus

1

Penso che una funzione di supporto, come suggerito da chi pone la domanda iniziale e Bryan Head, sia buona e non ingombrante. Una singola riga di codice magico che fa tutto il lavoro non è sempre possibile, quindi una funzione di supporto è una soluzione perfetta se si desidera evitare i loop for. Tuttavia vorrei modificarlo per questo:

# A modified version of the helper function by the Question starter 
def spam(egg): 
    try: 
     return 1/egg, None 
    except ZeroDivisionError as err: 
     # handle division by zero error   
     return None, err 

L'output sarà questo [(1/1, None), (1/3, None), (None, ZeroDivisionError), (1/3, None), (1/2, None)]. Con questa risposta hai tutto il controllo per continuare nel modo che preferisci.