Python: Woher weiß ich, welche Art von Ausnahme aufgetreten ist?

228

Ich habe eine Funktion, die vom Hauptprogramm aufgerufen wird:

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

In der Mitte der Ausführung der Funktion wird jedoch eine Ausnahme ausgelöst, sodass zum exceptTeil gesprungen wird .

Wie kann ich genau sehen, was in der passiert ist someFunction() , das die Ausnahme verursacht hat?

Shang Wang
quelle
9
Verwenden Sie niemals Bare except:(ohne Bare raise), außer vielleicht einmal pro Programm und vorzugsweise nicht dann.
Mike Graham
Wenn Sie mehrere exceptKlauseln verwenden, müssen Sie den Ausnahmetyp nicht überprüfen. Dies geschieht normalerweise, um einem bestimmten Ausnahmetyp entsprechend zu handeln.
Rik Poggi
3
Wenn Sie sich für den Ausnahmetyp interessieren, haben Sie bereits überlegt, welche Ausnahmetypen logisch auftreten können.
Karl Knechtel
3
Innerhalb des exceptBlocks ist die Ausnahme über die sys.exc_info()Funktion verfügbar. Diese Funktion gibt ein Tupel mit drei Werten zurück, die Informationen zu der Ausnahme enthalten, die derzeit behandelt wird.
Piotr Dobrogost

Antworten:

383

Die anderen Antworten weisen alle darauf hin, dass Sie keine generischen Ausnahmen abfangen sollten, aber niemand scheint Ihnen sagen zu wollen, warum. Dies ist wichtig, um zu verstehen, wann Sie die "Regel" brechen können. Hier ist eine Erklärung. Grundsätzlich ist es so, dass Sie sich nicht verstecken:

Solange Sie darauf achten, keines dieser Dinge zu tun, ist es in Ordnung, die generische Ausnahme abzufangen. Sie können dem Benutzer beispielsweise auf andere Weise Informationen über die Ausnahme bereitstellen, z.

  • Präsentieren Sie Ausnahmen als Dialoge in einer GUI
  • Übertragen Sie Ausnahmen von einem Worker-Thread oder -Prozess an den steuernden Thread oder Prozess in einer Multithreading- oder Multiprocessing-Anwendung

Wie kann man also die generische Ausnahme abfangen? Es gibt verschiedene Möglichkeiten. Wenn Sie nur das Ausnahmeobjekt möchten, gehen Sie folgendermaßen vor:

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

Stellen Sie sicher, dass message der Benutzer auf eine kaum zu übersehende Weise darauf aufmerksam gemacht wird! Das Drucken wie oben gezeigt reicht möglicherweise nicht aus, wenn die Nachricht in vielen anderen Nachrichten vergraben ist. Wenn Sie die Aufmerksamkeit des Benutzers nicht auf sich ziehen, bedeutet dies, dass Sie alle Ausnahmen verschlucken. Wenn Sie nach dem Lesen der Antworten auf dieser Seite einen Eindruck hinterlassen sollten, ist dies keine gute Sache . Durch Beenden des Ausnahmeblocks mit einer raiseAnweisung wird das Problem behoben, indem die abgefangene Ausnahme transparent erneut ausgelöst wird.

Der Unterschied zwischen dem oben Gesagten und der Verwendung except:ohne Argument ist zweifach:

  • Ein Bare except:gibt Ihnen nicht das Ausnahmeobjekt, das überprüft werden soll
  • Die Ausnahmen SystemExit, KeyboardInterruptund GeneratorExitwerden nicht durch den oben genannten Code gefangen, die in der Regel ist das, was Sie wollen. Siehe die Ausnahmehierarchie .

Wenn Sie auch denselben Stacktrace möchten, den Sie erhalten, wenn Sie die Ausnahme nicht abfangen, können Sie diesen wie folgt erhalten (immer noch in der Ausnahmeklausel enthalten):

import traceback
print traceback.format_exc()

Wenn Sie das loggingModul verwenden, können Sie die Ausnahme (zusammen mit einer Nachricht) wie folgt in das Protokoll drucken:

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

Wenn Sie tiefer graben und den Stapel untersuchen, Variablen usw. betrachten möchten, verwenden Sie die post_mortemFunktion des pdbModuls im Ausnahmeblock:

import pdb
pdb.post_mortem()

Ich habe festgestellt, dass diese letzte Methode bei der Suche nach Fehlern von unschätzbarem Wert ist.

Lauritz V. Thaulow
quelle
1
traceback.print_exc () würde das Gleiche tun wie Ihr komplizierteres "" .join-Ding, denke ich.
Gurgeh
1
@ Gurgeh Ja, aber ich weiß nicht, ob er es drucken oder in einer Datei speichern oder protokollieren oder etwas anderes damit machen möchte.
Lauritz V. Thaulow
Ich habe nicht herabgestimmt, aber ich würde sagen, das liegt daran, dass Sie am Anfang eine große fette Sorge hätten haben sollen, dass Sie nichts davon brauchen sollten, aber hier ist, wie es gemacht werden könnte . Und vielleicht, weil Sie vorschlagen, die generische Ausnahme zu fangen.
Rik Poggi
10
@ Rik Ich denke, du brauchst das alles sehr gut . Wenn Sie beispielsweise ein Programm mit einer GUI und einem Backend haben und alle Ausnahmen vom Backend als GUI-Nachrichten darstellen möchten, anstatt Ihr Programm mit einem Stack-Trace beenden zu lassen. In einem solchen Fall sollten Sie die generische Ausnahme abfangen , einen Traceback-Text für das Dialogfeld erstellen, die Ausnahme ebenfalls protokollieren und im Debug-Modus Post-Mortem eingeben.
Lauritz V. Thaulow
18
@ RikPoggi: Naives Denken. Es gibt viele vernünftige Umstände, unter denen Sie Ausnahmen vom Code eines anderen abfangen müssen und nicht wissen, welche Ausnahmen ausgelöst werden.
stackoverflowuser2010
63

Rufen Sie den Namen der Klasse ab, zu der das Ausnahmeobjekt gehört:

e.__class__.__name__

Bei Verwendung der Funktion print_exc () wird auch die Stapelverfolgung gedruckt, die für jede Fehlermeldung von wesentlicher Bedeutung ist.

So was:

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!"

Sie erhalten folgende Ausgabe:

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

Und nach dem Drucken und Analysieren kann der Code entscheiden, keine Ausnahme zu behandeln und nur Folgendes auszuführen 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"

Ausgabe:

special case of CustomException not interfering

Und Dolmetscher druckt Ausnahme:

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

Nach der raiseursprünglichen Ausnahme wird die Weitergabe des Aufrufstapels fortgesetzt. ( Vorsicht vor möglichen Gefahren ) Wenn Sie eine neue Ausnahme auslösen, wird eine neue (kürzere) Stapelspur erstellt.

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"

Ausgabe:

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    

Beachten Sie, dass Traceback keine calculate()Funktion von Zeile enthält, 9die der Ursprung der ursprünglichen Ausnahme ist e.

Alex
quelle
Wenn Sie den Traceback als String speichern möchten, können Sie traceback.format_exc()auch
Stevoisiak
1
e.__class__.__name__Ist dies dasselbe wie in type(e).__name__der obigen Antwort vorgeschlagen?
information_interchange
1
@information_interchange ja. Der Inhalt der Frage und der akzeptierten Antwort hat sich im Laufe der Zeit vollständig geändert. Es ist schade, dass andere Teilnehmer nicht von SO Machinery benachrichtigt werden :(
Alex
14

Normalerweise sollten Sie nicht alle möglichen Ausnahmen abfangen, try: ... exceptda diese zu weit gefasst sind. Fangen Sie einfach diejenigen, von denen erwartet wird, dass sie aus irgendeinem Grund passieren. Wenn Sie dies wirklich müssen, z. B. wenn Sie beim Debuggen mehr über ein Problem erfahren möchten, sollten Sie dies tun

try:
    ...
except Exception as ex:
    print ex # do whatever you want for debugging.
    raise    # re-raise exception.
hochl
quelle
17
Die Verwendung des Wortes "nie" war hier noch nie so falsch. Ich benutze try: ... except Exception:viele Dinge, z. B. die Verwendung netzwerkabhängiger Bibliotheken oder eine Datenmasseurin, die möglicherweise seltsame Dinge an sie sendet. Natürlich habe ich auch die richtige Protokollierung. Dies ist entscheidend, damit das Programm im Falle eines einzelnen Fehlers in den Eingabedaten weiterarbeiten kann.
Knie
3
Haben Sie jemals versucht, alle Ausnahmen zu erfassen, die beim Senden einer E-Mail mit auftreten können smtplib?
Linusg
1
Es kann einige Sonderfälle geben, in denen das Abfangen aller Ausnahmen erforderlich ist. Im Allgemeinen sollten Sie jedoch nur das abfangen, was Sie erwarten, damit Sie nicht versehentlich Fehler verbergen, die Sie nicht erwartet haben. Eine gute Protokollierung ist natürlich auch eine gute Idee.
hochl
1
Es ist durchaus sinnvoll, alle Ausnahmen zu erfassen. Wenn Sie eine Bibliothek eines Drittanbieters aufrufen, wissen Sie möglicherweise nicht, welche Ausnahmen in dieser Bibliothek ausgelöst werden. In einem solchen Fall besteht die einzige Möglichkeit darin, alle Ausnahmen abzufangen, beispielsweise um sie in einer Datei zu protokollieren.
stackoverflowuser2010
Ok ok du hast recht, ich werde meine Antwort umformulieren, um klar zu machen, dass es gültige Anwendungsfälle für alle gibt.
hochl
10

Sofern somefunctiones sich nicht um eine sehr schlecht codierte Legacy-Funktion handelt, sollten Sie nicht das benötigen, was Sie verlangen.

Verwenden Sie mehrere exceptKlauseln, um verschiedene Ausnahmen auf unterschiedliche Weise zu behandeln:

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

Der Hauptpunkt ist, dass Sie keine generische Ausnahme abfangen sollten, sondern nur die, die Sie benötigen. Ich bin sicher, dass Sie keine unerwarteten Fehler oder Bugs beschatten möchten.

Rik Poggi
quelle
8
Wenn Sie eine Bibliothek eines Drittanbieters verwenden, wissen Sie möglicherweise nicht, welche Ausnahmen darin ausgelöst werden. Wie können Sie möglicherweise alle einzeln fangen?
stackoverflowuser2010
8

Die meisten Antworten verweisen except (…) as (…):(zu Recht) auf die Syntax, aber gleichzeitig möchte niemand über einen Elefanten in dem Raum sprechen, in dem der Elefant sys.exc_info()funktioniert. Aus der Dokumentation des sys- Moduls (Schwerpunkt Mine):

Diese Funktion gibt ein Tupel mit drei Werten zurück, die Informationen zu der aktuell behandelten Ausnahme enthalten.
(…)
Wenn keine Ausnahme auf dem Stapel behandelt wird, wird ein Tupel mit drei Werten für Keine zurückgegeben. Andernfalls werden folgende Werte zurückgegeben (Typ, Wert, Rückverfolgung). Ihre Bedeutung ist: type ruft den Typ der Ausnahme ab, die behandelt wird (eine Unterklasse von BaseException); value ruft die Ausnahmeinstanz ab (eine Instanz des Ausnahmetyps); traceback erhält ein Traceback-Objekt (siehe Referenzhandbuch), das den Aufrufstapel an dem Punkt kapselt, an dem die Ausnahme ursprünglich aufgetreten ist.

Ich denke, das sys.exc_info()könnte als die direkteste Antwort auf die ursprüngliche Frage behandelt werden: Woher weiß ich, welche Art von Ausnahme aufgetreten ist?

Piotr Dobrogost
quelle
1
Das ist die richtige Antwort auf mich , da es das Problem der nicht löst , was Ausnahme auftritt, so etwas sollte ich statt nackten setzen except. Der Vollständigkeit halber exctype, value = sys.exc_info()[:2]wird Ihnen der Ausnahmetyp mitgeteilt, der dann auf dem verwendet werden kann except.
Ondrej Burkert
5

try: someFunction () außer 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))
wp-overwatch.com
quelle
-1 als Verwendung exc.__class__.__name__wurde bereits in Alex 'Antwort vorgeschlagen - stackoverflow.com/a/9824060/95735
Piotr Dobrogost
2

Diese Antworten eignen sich gut zum Debuggen, aber zum programmgesteuerten Testen der Ausnahme isinstance(e, SomeException)können sie nützlich sein, da sie auch auf Unterklassen von testen SomeException, sodass Sie Funktionen erstellen können, die für Hierarchien von Ausnahmen gelten.

Chris
quelle
1

So gehe ich mit meinen Ausnahmen um. Die Idee ist, das Problem zu lösen, wenn dies einfach ist, und später, wenn möglich, eine wünschenswertere Lösung hinzuzufügen. Lösen Sie das Problem nicht in dem Code, der die Ausnahme generiert, oder dieser Code verliert den Überblick über den ursprünglichen Algorithmus, der auf den Punkt geschrieben werden sollte. Übergeben Sie jedoch, welche Daten zur Lösung des Problems benötigt werden, und geben Sie ein Lambda zurück, falls Sie das Problem nicht außerhalb des Codes lösen können, der es generiert.

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

Im Moment habe ich keine komplizierten Lösungen hinzugefügt, da ich nicht tangential zum Zweck meiner App denken möchte. Wenn ich in Zukunft mehr über mögliche Lösungen weiß (da die App mehr entwickelt wurde), könnte ich ein Wörterbuch mit Lösungen hinzufügen, die von indiziert werden during.

In dem gezeigten Beispiel könnte eine Lösung darin bestehen, nach App-Daten zu suchen, die an einer anderen Stelle gespeichert sind, beispielsweise wenn die Datei 'app.p' versehentlich gelöscht wurde.

Da das Schreiben des Ausnahmehandlers keine kluge Idee ist (wir kennen die besten Lösungsmöglichkeiten noch nicht, da sich das App-Design weiterentwickeln wird), geben wir einfach die einfache Lösung zurück, die sich so verhält, als würden wir ausgeführt die App zum ersten Mal (in diesem Fall).

AbstractAlgebraLearner
quelle
0

Um Lauritz 'Antwort zu ergänzen, habe ich einen Dekorator / Wrapper für die Ausnahmebehandlung erstellt und der Wrapper protokolliert, welcher Ausnahmetyp aufgetreten ist.

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

Dies kann für eine Klassenmethode oder eine eigenständige Funktion mit dem Dekorator aufgerufen werden:

@general_function_handler

Das vollständige Beispiel finden Sie in meinem Blog über: http://ryaneirwin.wordpress.com/2014/05/31/python-decorators-and-exception-handling/

Rirwin
quelle
0

Sie können wie von Lauritz empfohlen beginnen mit:

except Exception as ex:

und dann einfach so zu print exmögen:

try:
    #your try code here
except Exception as ex:
    print ex
Gura
quelle
Können Sie etwas näher darauf eingehen, damit Ihre Antwort allein steht?
GHC
1
sicher: Sie können die abgefangene Ausnahme wie folgt drucken: try: #Ihr try-Code hier außer Ausnahme als ex: print ex jetzt wird der Fehler gedruckt
Gura
-2

Die eigentliche Ausnahme kann folgendermaßen erfasst werden:

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

Weitere Informationen zu Ausnahmen finden Sie im Python-Tutorial .

Kevin Coffey
quelle
-2

Ihre Frage lautet: "Wie kann ich genau sehen, was in someFunction () passiert ist, das die Ausnahme verursacht hat?"

Es scheint mir, dass Sie nicht fragen, wie Sie mit unvorhergesehenen Ausnahmen im Produktionscode umgehen sollen (wie viele Antworten angenommen haben), sondern wie Sie herausfinden, was eine bestimmte Ausnahme während der Entwicklung verursacht.

Am einfachsten ist es, einen Debugger zu verwenden, der dort anhalten kann, wo die nicht erfasste Ausnahme auftritt, vorzugsweise nicht beendet wird, damit Sie die Variablen überprüfen können. Dies kann beispielsweise PyDev in der Open Source-IDE von Eclipse. Um dies in Eclipse zu aktivieren, öffnen Sie die Debug-Perspektive, wählen Sie Manage Python Exception Breakpointsim RunMenü aus und überprüfen Sie Suspend on uncaught exceptions.


quelle
-4

Vermeiden Sie es einfach, die Ausnahme abzufangen, und der Traceback, den Python druckt, zeigt Ihnen, welche Ausnahme aufgetreten ist.

Mike Graham
quelle