python: come faccio a sapere quale tipo di eccezione si è verificata?


230

Ho una funzione chiamata dal programma principale:

try:
    someFunction()
except:
    print "exception happened!"

ma nel mezzo dell'esecuzione della funzione solleva un'eccezione, quindi salta alla exceptparte.

Come posso vedere esattamente cosa è successo a ciò someFunction()che ha causato l'eccezione?


9
Mai e poi mai usare bare except:(senza uno bare raise), tranne forse una volta per programma, e preferibilmente non allora.
Mike Graham,

Se si utilizzano più exceptclausole, non è necessario controllare il tipo di eccezione, ovvero ciò che viene solitamente fatto per agire di conseguenza in base a un tipo di eccezione specifico.
Rik Poggi,

3
Se ti interessa il tipo di eccezione, è perché hai già considerato quali tipi di eccezione potrebbero verificarsi logicamente.
Karl Knechtel,

3
All'interno del exceptblocco l'eccezione è disponibile tramite la sys.exc_info()funzione: questa funzione restituisce una tupla di tre valori che forniscono informazioni sull'eccezione attualmente gestita.
Piotr Dobrogost,

Risposte:


384

Le altre risposte sottolineano che non dovresti cogliere eccezioni generiche, ma nessuno sembra voler dirti perché, il che è essenziale per capire quando puoi infrangere la "regola". Ecco una spiegazione Fondamentalmente, è così che non ti nascondi:

Quindi, fintanto che ti occupi di fare nulla di tutto ciò, è OK catturare l'eccezione generica. Ad esempio, potresti fornire all'utente informazioni sull'eccezione in un altro modo, ad esempio:

  • Presentare le eccezioni come finestre di dialogo in una GUI
  • Trasferire le eccezioni da un thread o processo di lavoro al thread o al processo di controllo in un'applicazione multithreading o multiprocessing

Quindi, come catturare l'eccezione generica? Esistono diversi modi. Se vuoi solo l'oggetto eccezione, fallo in questo modo:

try:
    someFunction()
except Exception as ex:
    template = "An exception of type {0} occurred. Arguments:\n{1!r}"
    message = template.format(type(ex).__name__, ex.args)
    print message

Assicurati che message sia portato all'attenzione dell'utente in un modo difficile da perdere! Stamparlo, come mostrato sopra, potrebbe non essere sufficiente se il messaggio è sepolto in molti altri messaggi. Non riuscire ad attirare l'attenzione degli utenti equivale a ingoiare tutte le eccezioni, e se c'è un'impressione che avresti dovuto scappare dopo aver letto le risposte in questa pagina, è che non è una buona cosa . Terminare il blocco di esclusione con raiseun'istruzione risolverà il problema rilanciando in modo trasparente l'eccezione rilevata.

La differenza tra quanto sopra e l'utilizzo solo except:senza alcun argomento è duplice:

  • Un nudo except:non ti dà l'oggetto eccezione da ispezionare
  • Le eccezioni SystemExit, KeyboardInterrupte GeneratorExitnon sono colte dal codice sopra, che è generalmente quello che vuoi. Vedi la gerarchia delle eccezioni .

Se vuoi anche lo stesso stacktrace che ottieni se non catturi l'eccezione, puoi ottenerlo in questo modo (sempre all'interno della clausola tranne):

import traceback
print traceback.format_exc()

Se si utilizza il loggingmodulo, è possibile stampare l'eccezione nel registro (insieme a un messaggio) in questo modo:

import logging
log = logging.getLogger()
log.exception("Message for you, sir!")

Se vuoi scavare più a fondo ed esaminare lo stack, guardare le variabili ecc., Usa la post_mortemfunzione del pdbmodulo all'interno del blocco tranne:

import pdb
pdb.post_mortem()

Ho trovato questo ultimo metodo prezioso per la ricerca di bug.


1
traceback.print_exc () farebbe la stessa cosa della tua "" .join-cosa più complicata, credo.
Gurgeh,

1
@Gurgeh Sì, ma non so se vuole stamparlo o salvarlo su un file o registrarlo o fare qualcos'altro con esso.
Lauritz V. Thaulow,

Non ho votato per il downgrade, ma direi che dovresti aver dato un gran peso all'inizio dicendo che non dovresti aver bisogno di tutto questo, ma ecco come si potrebbe fare . E forse perché suggerisci di catturare l'eccezione generica.
Rik Poggi,

10
@Rik Penso che potresti aver bisogno di tutto questo. Ad esempio, se si dispone di un programma con una GUI e un back-end e si desidera presentare tutte le eccezioni dal back-end come messaggi della GUI anziché interrompere il programma con una traccia dello stack. In tal caso, dovresti prendere l' eccezione generica , creare un testo di traceback per la finestra di dialogo, registrare anche l'eccezione e, in modalità debug, inserire post-mortem.
Lauritz V. Thaulow,

18
@RikPoggi: pensiero ingenuo. Ci sono molte circostanze ragionevoli in cui è necessario rilevare le eccezioni dal codice di qualcun altro e non si sa quali eccezioni verranno sollevate.
stackoverflowuser2010

63

Ottieni il nome della classe a cui appartiene l'oggetto eccezione:

e.__class__.__name__

e usando la funzione print_exc () si stamperà anche la traccia dello stack che è informazione essenziale per qualsiasi messaggio di errore.

Come questo:

from traceback import print_exc

class CustomException(Exception): pass

try:
    raise CustomException("hi")
except Exception, e:
    print 'type is:', e.__class__.__name__
    print_exc()
    # print "exception happened!"

Otterrai un output in questo modo:

type is: CustomException
Traceback (most recent call last):
  File "exc.py", line 7, in <module>
    raise CustomException("hi")
CustomException: hi

E dopo la stampa e l'analisi, il codice può decidere di non gestire le eccezioni ed eseguire semplicemente raise:

from traceback import print_exc

class CustomException(Exception): pass

def calculate():
    raise CustomException("hi")

try:
    calculate()
except Exception, e:
    if e.__class__ == CustomException:
        print 'special case of', e.__class__.__name__, 'not interfering'
        raise
    print "handling exception"

Produzione:

special case of CustomException not interfering

E l'interprete stampa l'eccezione:

Traceback (most recent call last):
  File "test.py", line 9, in <module>
    calculate()
  File "test.py", line 6, in calculate
    raise CustomException("hi")
__main__.CustomException: hi

Dopo l' raiseeccezione originale continua a propagarsi ulteriormente nello stack di chiamate. ( Attenzione alle possibili insidie ) Se si solleva una nuova eccezione, si trascina una nuova traccia dello stack (più breve).

from traceback import print_exc

class CustomException(Exception): pass

def calculate():
    raise CustomException("hi")

try:
    calculate()
except Exception, e:
    if e.__class__ == CustomException:
        print 'special case of', e.__class__.__name__, 'not interfering'
        #raise CustomException(e.message)
        raise e
    print "handling exception"

Produzione:

special case of CustomException not interfering
Traceback (most recent call last):
  File "test.py", line 13, in <module>
    raise CustomException(e.message)
__main__.CustomException: hi    

Notare come il traceback non include la calculate()funzione dalla riga 9che è l'origine dell'eccezione originale e.


Se si desidera memorizzare il traceback come stringa, è possibile utilizzare traceback.format_exc()anche
Stevoisiak,

1
e.__class__.__name__è lo stesso type(e).__name__, come suggerito dalla risposta sopra?
information_interchange il

1
@information_interchange yes. La domanda e il contenuto della risposta accettata sono cambiati completamente nel tempo. È un peccato che gli altri partecipanti non siano informati dai macchinari SO :(
Alex

14

Di solito non dovresti cogliere tutte le possibili eccezioni try: ... exceptpoiché è eccessivamente ampio. Prendi solo quelli che dovrebbero accadere per qualsiasi motivo. Se è necessario, ad esempio se si desidera saperne di più su alcuni problemi durante il debug, è necessario farlo

try:
    ...
except Exception as ex:
    print ex # do whatever you want for debugging.
    raise    # re-raise exception.

17
L'uso della parola "mai" qui non è mai stato così sbagliato. Uso try: ... except Exception:molte cose, ad esempio l'uso di librerie dipendenti dalla rete, o una massaggiatrice di dati che potrebbe ricevere cose strane che le vengono inviate. Naturalmente ho anche una registrazione adeguata. Ciò è fondamentale per consentire al programma di continuare a funzionare in caso di un singolo errore nei dati di input.
Grazie,

3
Hai mai provato a cogliere tutte le eccezioni che potrebbero essere sollevate durante l'invio di un'e-mail utilizzando smtplib?
linusg,

1
Ci possono essere alcuni casi speciali in cui è necessario rilevare tutte le eccezioni, ma a livello generale dovresti solo catturare ciò che ti aspetti in modo da non nascondere accidentalmente errori che non avevi previsto. Anche una buona registrazione è una buona idea, ovviamente.
Hochl,

1
È perfettamente ragionevole cogliere tutte le eccezioni. Se stai chiamando una libreria di terze parti, potresti non sapere quali eccezioni verranno sollevate in quella libreria. In tal caso, l'unica soluzione è catturare tutte le eccezioni, ad esempio per registrarle in un file.
stackoverflowuser2010

Ok ok hai ragione, riformulerò la mia risposta per chiarire che ci sono casi d'uso validi per un fermo tutto.
Hochl,

10

A meno che non somefunctionsia una pessima funzione legacy codificata, non dovresti aver bisogno di ciò che stai chiedendo.

Utilizzare più exceptclausole per gestire in modo diverso diverse eccezioni:

try:
    someFunction()
except ValueError:
    # do something
except ZeroDivision:
    # do something else

Il punto principale è che non dovresti prendere un'eccezione generica, ma solo quelle di cui hai bisogno. Sono sicuro che non vuoi ombreggiare errori o bug imprevisti.


8
Se stai utilizzando una libreria di terze parti, potresti non sapere quali eccezioni verranno sollevate al suo interno. Come puoi prenderli tutti individualmente?
stackoverflowuser2010

8

La maggior parte delle risposte indica except (…) as (…):sintassi (giustamente) ma allo stesso tempo nessuno vuole parlare di un elefante nella stanza, dove l'elefante è sys.exc_info()funzione. Dalla documentazione del modulo sys (sottolineatura mia):

Questa funzione restituisce una tupla di tre valori che forniscono informazioni sull'eccezione attualmente gestita.
(...)
Se non viene gestita alcuna eccezione in qualsiasi punto dello stack, viene restituita una tupla contenente tre valori Nessuno. Altrimenti, i valori restituiti sono (tipo, valore, traceback). Il loro significato è: type ottiene il tipo di eccezione gestita (una sottoclasse di BaseException); valore ottiene l'istanza dell'eccezione (un'istanza del tipo di eccezione); traceback ottiene un oggetto traceback (consultare il Manuale di riferimento) che incapsula lo stack di chiamate nel punto in cui si è verificata originariamente l'eccezione.

Penso che sys.exc_info()potrebbe essere considerata la risposta più diretta alla domanda originale di Come faccio a sapere che tipo di eccezione si è verificata?


1
Questa è la risposta corretta per me in quanto risolve il problema di quale eccezione si sta verificando, quindi cosa devo mettere invece di nudo except. Solo per completezza, exctype, value = sys.exc_info()[:2]ti dirò il tipo di eccezione che può essere utilizzato sul except.
Ondrej Burkert,

5

provare: someFunction () tranne Exception, exc:

#this is how you get the type
excType = exc.__class__.__name__

#here we are printing out information about the Exception
print 'exception type', excType
print 'exception msg', str(exc)

#It's easy to reraise an exception with more information added to it
msg = 'there was a problem with someFunction'
raise Exception(msg + 'because of %s: %s' % (excType, exc))

-1 come l'utilizzo exc.__class__.__name__era già stato suggerito nella risposta di Alex - stackoverflow.com/a/9824060/95735
Piotr Dobrogost

3

Queste risposte vanno bene per il debug, ma per testare l'eccezione a livello di programmazione, isinstance(e, SomeException)possono essere utili, poiché verifica anche le sottoclassi SomeException, in modo da poter creare funzionalità che si applicano alle gerarchie di eccezioni.


1

Ecco come sto gestendo le mie eccezioni. L'idea è di provare a risolvere il problema, se è facile, e successivamente aggiungere una soluzione più desiderabile, se possibile. Non risolvere il problema nel codice che genera l'eccezione o che il codice perde traccia dell'algoritmo originale, che dovrebbe essere scritto nel punto. Tuttavia, passa i dati necessari per risolvere il problema e restituisci un lambda nel caso in cui non sia possibile risolvere il problema al di fuori del codice che lo genera.

path = 'app.p'

def load():
    if os.path.exists(path):
        try:
            with open(path, 'rb') as file:
                data = file.read()
                inst = pickle.load(data)
        except Exception as e:
            inst = solve(e, 'load app data', easy=lambda: App(), path=path)()
    else:
        inst = App()
    inst.loadWidgets()

# e.g. A solver could search for app data if desc='load app data'
def solve(e, during, easy, **kwargs):
    class_name = e.__class__.__name__
    print(class_name + ': ' + str(e))
    print('\t during: ' + during)
    return easy

Per ora, dal momento che non voglio pensare tangenzialmente allo scopo della mia app, non ho aggiunto soluzioni complicate. Ma in futuro, quando ne saprò di più sulle possibili soluzioni (poiché l'app è progettata di più), potrei aggiungere un dizionario di soluzioni indicizzato daduring .

Nell'esempio mostrato, una soluzione potrebbe essere quella di cercare i dati delle app memorizzati altrove, ad esempio se il file 'app.p' è stato eliminato per errore.

Per ora, dal momento che scrivere il gestore delle eccezioni non è un'idea intelligente (non conosciamo ancora i modi migliori per risolverlo, poiché il design dell'app si evolverà), restituiamo semplicemente la soluzione semplice che deve agire come se fossimo in esecuzione l'app per la prima volta (in questo caso).


0

Per aggiungere la risposta di Lauritz, ho creato un decoratore / wrapper per la gestione delle eccezioni e i registri del wrapper che tipo di eccezione si è verificata.

class general_function_handler(object):
    def __init__(self, func):
        self.func = func
    def __get__(self, obj, type=None):
        return self.__class__(self.func.__get__(obj, type))
    def __call__(self, *args, **kwargs):
        try:
            retval = self.func(*args, **kwargs)
        except Exception, e :
            logging.warning('Exception in %s' % self.func)
            template = "An exception of type {0} occured. Arguments:\n{1!r}"
            message = template.format(type(e).__name__, e.args)
            logging.exception(message)
            sys.exit(1) # exit on all exceptions for now
        return retval

Questo può essere chiamato su un metodo di classe o una funzione autonoma con il decoratore:

@general_function_handler

Vedi il mio blog per l'esempio completo: http://ryaneirwin.wordpress.com/2014/05/31/python-decorators-and-exception-handling/


0

Puoi iniziare come consigliato da Lauritz, con:

except Exception as ex:

e poi solo per print expiacere:

try:
    #your try code here
except Exception as ex:
    print ex

Puoi elaborare un po 'in modo che la tua risposta sia sola?
GHC,

1
certo: puoi stampare l'eccezione rilevata in questo modo: prova: #il tuo codice di prova qui tranne Eccezione come es: stampa ex ora l'errore verrà stampato
Gura

-2

L'eccezione effettiva può essere acquisita nel modo seguente:

try:
    i = 1/0
except Exception as e:
    print e

Puoi saperne di più sulle eccezioni da The Python Tutorial .


-2

La tua domanda è: "Come posso vedere esattamente cosa è successo in someFunction () che ha causato l'eccezione?"

Mi sembra che non ti stia chiedendo come gestire le eccezioni impreviste nel codice di produzione (come molte risposte presunte), ma come scoprire cosa sta causando una particolare eccezione durante lo sviluppo.

Il modo più semplice è utilizzare un debugger che può arrestare il punto in cui si verifica l'eccezione non rilevata, preferibilmente non uscire, in modo da poter ispezionare le variabili. Ad esempio, PyDev nell'IDE open source Eclipse può farlo. Per abilitarlo in Eclipse, apri la prospettiva Debug, seleziona Manage Python Exception Breakpointsnel Runmenu e controlla Suspend on uncaught exceptions.


-4

Astenersi dal catturare l'eccezione e il traceback che Python stampa ti dirà quale eccezione si è verificata.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.