Manuelles Auslösen (Auslösen) einer Ausnahme in Python

2264

Wie kann ich eine Ausnahme in Python auslösen, damit sie später über einen exceptBlock abgefangen werden kann ?

TIMEX
quelle

Antworten:

2937

Wie kann ich eine Ausnahme in Python manuell auslösen / auslösen?

Verwenden Sie den spezifischsten Ausnahmekonstruktor, der semantisch zu Ihrem Problem passt .

Seien Sie in Ihrer Nachricht genau, z. B.:

raise ValueError('A very specific bad thing happened.')

Erhöhen Sie keine generischen Ausnahmen

Vermeiden Sie es, ein Generikum zu erheben Exception. Um es abzufangen, müssen Sie alle anderen spezifischeren Ausnahmen abfangen, die es unterordnen.

Problem 1: Fehler verstecken

raise Exception('I know Python!') # Don't! If you catch, likely to hide bugs.

Zum Beispiel:

def demo_bad_catch():
    try:
        raise ValueError('Represents a hidden bug, do not catch this')
        raise Exception('This is the exception you expect to handle')
    except Exception as error:
        print('Caught this error: ' + repr(error))

>>> demo_bad_catch()
Caught this error: ValueError('Represents a hidden bug, do not catch this',)

Problem 2: Wird nicht fangen

Und spezifischere Fänge werden die allgemeine Ausnahme nicht erfassen:

def demo_no_catch():
    try:
        raise Exception('general exceptions not caught by specific handling')
    except ValueError as e:
        print('we will not catch exception: Exception')


>>> demo_no_catch()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in demo_no_catch
Exception: general exceptions not caught by specific handling

Best Practices: raiseAussage

Verwenden Sie stattdessen den spezifischsten Ausnahmekonstruktor, der semantisch zu Ihrem Problem passt .

raise ValueError('A very specific bad thing happened')

Dies ermöglicht auch die Übergabe einer beliebigen Anzahl von Argumenten an den Konstruktor:

raise ValueError('A very specific bad thing happened', 'foo', 'bar', 'baz') 

Auf diese Argumente wird über das argsAttribut des ExceptionObjekts zugegriffen . Zum Beispiel:

try:
    some_code_that_may_raise_our_value_error()
except ValueError as err:
    print(err.args)

druckt

('message', 'foo', 'bar', 'baz')    

In Python 2.5, ein tatsächliches messagewurde Attribut hinzugefügt , um BaseExceptionfür den Einsatz von Benutzern Unterklasse Ausnahmen und Stop ermutigend args, aber die Einführung messageund die ursprüngliche deprecation von args zurückgezogen worden ist .

Best Practices: exceptKlausel

Wenn Sie sich in einer Ausnahmeklausel befinden, möchten Sie beispielsweise protokollieren, dass ein bestimmter Fehlertyp aufgetreten ist, und ihn dann erneut auslösen. Der beste Weg, dies unter Beibehaltung des Stack-Trace zu tun, ist die Verwendung einer Bare-Raise-Anweisung. Zum Beispiel:

logger = logging.getLogger(__name__)

try:
    do_something_in_app_that_breaks_easily()
except AppError as error:
    logger.error(error)
    raise                 # just this!
    # raise AppError      # Don't do this, you'll lose the stack trace!

Ändern Sie Ihre Fehler nicht ... aber wenn Sie darauf bestehen.

Sie können den Stacktrace (und den Fehlerwert) mit beibehalten sys.exc_info(), dies ist jedoch weitaus fehleranfälliger und weist Kompatibilitätsprobleme zwischen Python 2 und 3 auf. Verwenden Sie raisezum erneuten Erhöhen lieber ein Bare .

Zur Erklärung: sys.exc_info()Gibt den Typ, den Wert und den Traceback zurück.

type, value, traceback = sys.exc_info()

Dies ist die Syntax in Python 2 - beachten Sie, dass dies nicht mit Python 3 kompatibel ist:

    raise AppError, error, sys.exc_info()[2] # avoid this.
    # Equivalently, as error *is* the second object:
    raise sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]

Wenn Sie möchten, können Sie ändern, was mit Ihrer neuen Erhöhung geschieht - z. B. neue Einstellungen argsfür die Instanz:

def error():
    raise ValueError('oops!')

def catch_error_modify_message():
    try:
        error()
    except ValueError:
        error_type, error_instance, traceback = sys.exc_info()
        error_instance.args = (error_instance.args[0] + ' <modification>',)
        raise error_type, error_instance, traceback

Und wir haben den gesamten Traceback beibehalten, während wir die Argumente geändert haben. Beachten Sie, dass dies keine bewährte Methode ist und die Syntax in Python 3 ungültig ist (was die Beibehaltung der Kompatibilität erheblich erschwert).

>>> catch_error_modify_message()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in catch_error_modify_message
  File "<stdin>", line 2, in error
ValueError: oops! <modification>

In Python 3 :

    raise error.with_traceback(sys.exc_info()[2])

Nochmals: Vermeiden Sie die manuelle Bearbeitung von Tracebacks. Es ist weniger effizient und fehleranfälliger. Und wenn Sie Threading verwenden und sys.exc_infomöglicherweise sogar das falsche Traceback erhalten (insbesondere, wenn Sie die Ausnahmebehandlung für den Kontrollfluss verwenden - was ich persönlich eher vermeiden würde).

Python 3, Ausnahmeverkettung

In Python 3 können Sie Ausnahmen verketten, die Tracebacks beibehalten:

    raise RuntimeError('specific message') from error

Sei vorsichtig:

  • dies nicht erlauben , der Fehlertyp ändert erhöht und
  • Dies ist nicht kompatibel mit Python 2.

Veraltete Methoden:

Diese können sich leicht verstecken und sogar in den Produktionscode gelangen. Sie möchten eine Ausnahme auslösen, und wenn Sie dies tun, wird eine Ausnahme ausgelöst, aber nicht die beabsichtigte!

In Python 2, jedoch nicht in Python 3, gilt Folgendes:

raise ValueError, 'message' # Don't do this, it's deprecated!

Nur in viel älteren Versionen von Python (2.4 und niedriger) gültig , sehen Sie möglicherweise immer noch Leute, die Zeichenfolgen erhöhen:

raise 'message' # really really wrong. don't do this.

In allen modernen Versionen wird dadurch tatsächlich a ausgelöst TypeError, da Sie keinen BaseExceptionTyp erhöhen . Wenn Sie nicht nach der richtigen Ausnahme suchen und keinen Prüfer haben, der sich des Problems bewusst ist, kann es in die Produktion gehen.

Beispiel Verwendung

Ich löse Ausnahmen aus, um Verbraucher vor meiner API zu warnen, wenn sie diese falsch verwenden:

def api_func(foo):
    '''foo should be either 'baz' or 'bar'. returns something very useful.'''
    if foo not in _ALLOWED_ARGS:
        raise ValueError('{foo} wrong, use "baz" or "bar"'.format(foo=repr(foo)))

Erstellen Sie Ihre eigenen Fehlertypen, wenn Sie vorschlagen

"Ich möchte absichtlich einen Fehler machen, damit er in die Ausnahme geht."

Sie können Ihre eigenen Fehlertypen erstellen. Wenn Sie angeben möchten, dass etwas Bestimmtes mit Ihrer Anwendung nicht stimmt, klassifizieren Sie einfach den entsprechenden Punkt in der Ausnahmehierarchie:

class MyAppLookupError(LookupError):
    '''raise this when there's a lookup error for my app'''

und Verwendung:

if important_key not in resource_dict and not ok_to_be_missing:
    raise MyAppLookupError('resource is missing, and that is not ok.')
Aaron Hall
quelle
19
Danke dafür, es ist genau das, was ich brauchte. Das Nackte war raisedas, was ich brauchte, um ein benutzerdefiniertes Fehler-Debugging auf mehreren Ebenen der Codeausführung durchführen zu können, ohne den Stack-Trace zu unterbrechen.
CaffeineConnoisseur
Dies ist eine großartige Antwort. Aber ich arbeite immer noch mit viel 2.7-Code und möchte häufig Informationen zu einer unerwarteten Ausnahme hinzufügen, z. B. zu einer Eingabedateiposition oder den Werten einiger Variablen, aber den ursprünglichen Stapel und die Ausnahme beibehalten. Ich kann es protokollieren, aber manchmal möchte ich nicht, dass es protokolliert wird, z. B. wenn der übergeordnete Code letztendlich damit umgeht. raise sys.exc_info()[0], (sys.exc_info()[1], my_extra_info), sys.exc_info()[2]scheint zu tun, was ich will, und ich habe nie Probleme damit gehabt. Aber es fühlt sich hackig an und keine akzeptierte Praxis. Gibt es einen besseren Weg?
Michael Scheper
2
@brennanyoung In diesem Zusammenhang könnte es verwirrend sein, einen SyntaxError auszulösen - wahrscheinlich sollten Sie eine benutzerdefinierte Ausnahme auslösen. Ich erkläre hier, wie es geht: stackoverflow.com/a/26938914/541136
Aaron Hall
2
Beachten Sie, dass das vollständige Anführungszeichen "Alle integrierten, nicht systemexitierenden Ausnahmen werden von dieser Klasse abgeleitet. Alle benutzerdefinierten Ausnahmen sollten auch von dieser Klasse abgeleitet werden." - Das bedeutet meistens, dass Sie keine der 4 Ausnahmen verwenden sollten, die nicht von ExceptionIhrer Elternklasse abgeleitet sind. Sie können etwas Spezifischeres unterordnen und sollten dies tun, wenn es sinnvoll ist.
Aaron Hall
1
Im Beispiel für " Best Practices: Ausnahmeklausel " verwenden Sie eine undefinierte AppErrorAusnahme. Es kann besser sein, einen eingebauten Fehler wieAttributeError
Stevoisiak
530

TUN SIE DAS NICHT . Ein nacktes Kind aufzuziehen Exceptionist absolut nicht das Richtige. siehe stattdessen Aaron Halls ausgezeichnete Antwort .

Viel pythonischer geht es nicht:

raise Exception("I know python!")

Siehe die raise - Anweisung docs für Python , wenn Sie weitere Informationen möchten.

Gabriel Hurley
quelle
67
Nein, bitte! Dies beseitigt das Potenzial, spezifisch zu sein, was Sie fangen. Es ist VOLLSTÄNDIG der falsche Weg, dies zu tun. Schauen Sie sich stattdessen Aaron Halls hervorragende Antwort an. In solchen Zeiten wünschte ich mir, ich könnte mehr als eine Gegenstimme pro Antwort abgeben.
Dawood ibn Kareem
27
@PeterR Es ist genauso schrecklich, dass es so wenige Downvotes gibt. Um diese Antwort zu lesen, tun Sie dies niemals! Die richtige Antwort ist die von Aaron Hall.
Dawood ibn Kareem
6
Ich denke, es sollte eine detailliertere Erklärung geben, warum dies falsch oder so schlecht ist.
Charlie Parker
9
@ CharlieParker gibt es. Es ist der erste Teil von Aaron Halls Antwort .
Dinei
5
Warum kann diese Antwort nicht zum Löschen markiert werden? Es hat bereits 93 Downvotes!
Codeforester
54

In Python3 gibt es 4 verschiedene Syntaxen für das Auslösen von Ausnahmen:

1. raise exception 
2. raise exception (args) 
3. raise
4. raise exception (args) from original_exception

1. Ausnahme auslösen vs. 2. Ausnahme auslösen (Argumente)

Wenn Sie raise exception (args) eine Ausnahme auslösen, argswird diese beim Drucken des Ausnahmeobjekts gedruckt - wie im folgenden Beispiel gezeigt.

  #raise exception (args)
    try:
        raise ValueError("I have raised an Exception")
    except ValueError as exp:
        print ("Error", exp)     # Output -> Error I have raised an Exception 



  #raise execption 
    try:
        raise ValueError
    except ValueError as exp:
        print ("Error", exp)     # Output -> Error 

3. steigen

raiseAnweisung ohne Argumente löst die letzte Ausnahme erneut aus. Dies ist nützlich, wenn Sie nach dem Abfangen der Ausnahme einige Aktionen ausführen und sie dann erneut auslösen möchten. Wenn es zuvor keine Ausnahme gab, raiselöst die Anweisung eine TypeErrorAusnahme aus.

def somefunction():
    print("some cleaning")

a=10
b=0 
result=None

try:
    result=a/b
    print(result)

except Exception:            #Output ->
    somefunction()           #some cleaning
    raise                    #Traceback (most recent call last):
                             #File "python", line 8, in <module>
                             #ZeroDivisionError: division by zero

4. Ausnahme (args) von original_exception auslösen

Diese Anweisung wird verwendet, um eine Ausnahmeverkettung zu erstellen, bei der eine Ausnahme, die als Antwort auf eine andere Ausnahme ausgelöst wird, die Details der ursprünglichen Ausnahme enthalten kann - wie im folgenden Beispiel gezeigt.

class MyCustomException(Exception):
pass

a=10
b=0 
reuslt=None
try:
    try:
        result=a/b

    except ZeroDivisionError as exp:
        print("ZeroDivisionError -- ",exp)
        raise MyCustomException("Zero Division ") from exp

except MyCustomException as exp:
        print("MyException",exp)
        print(exp.__cause__)

Ausgabe:

ZeroDivisionError --  division by zero
MyException Zero Division 
division by zero
N Randhawa
quelle
7
Bitte beachten Sie, dass PEP8 lieber ist exception(args)alsexception (args)
Gloweye
Es ist auch raise exception(args) from Nonezu sagen, dass die derzeit aktive Ausnahme behandelt wurde und nicht mehr von Interesse ist. Andernfalls werden exceptTracebacks für beide Ausnahmen getrennt durch die Meldung "Während der Behandlung der obigen Ausnahme ist eine weitere Ausnahme aufgetreten"
angezeigt
35

Für den allgemeinen Fall, dass Sie als Reaktion auf einige unerwartete Bedingungen eine Ausnahme auslösen müssen und diese niemals abfangen möchten, sondern einfach schnell versagen müssen, damit Sie von dort aus debuggen können, falls dies jemals passieren sollte - die logischste scheint zu sein AssertionError::

if 0 < distance <= RADIUS:
    #Do something.
elif RADIUS < distance:
    #Do something.
else:
    raise AssertionError("Unexpected value of 'distance'!", distance)
Evgeni Sergeev
quelle
19
Dies ist ein besserer Fall, ValueErrorals AssertionErrorweil es kein Problem mit einer Behauptung gibt (weil hier keine gemacht wird) - das Problem liegt bei einem Wert. Wenn Sie AssertionErrorin diesem Fall wirklich eine wollen , schreiben Sie assert distance > 0, 'Distance must be positive'. Auf diese Weise sollten Sie jedoch keine Fehler überprüfen, da Zusicherungen deaktiviert werden können ( python -O).
Zwei-Bit-Alchemist
1
@ Two-BitAlchemist Guter Punkt. Die Idee ging in der Vereinfachung verloren, als ich das einfache Beispiel oben schrieb. In vielen ähnlichen Fällen handelt es sich um eine Bedingung, die keinem bestimmten Wert zugeordnet ist. Die Bedeutung ist vielmehr "Kontrollfluss sollte niemals hierher kommen".
Evgeni Sergeev
2
@ Two-BitAlchemist Assertions können deaktiviert werden, ja, aber dann sollten Sie sie überhaupt nicht zur Fehlerprüfung verwenden?
Evgeni Sergeev
Es hängt davon ab. Ich würde nicht zulassen, dass dies meine einzige Fehlerprüfung in einem Programm ist, das ich verteilen wollte. Andererseits könnte ich ein Programm nur für meine Mitarbeiter erstellen und ihnen sagen, dass sie es auf eigenes Risiko verwenden, wenn sie es ausführen -O.
Zwei-Bit-Alchemist
1
@ Two-BitAlchemist Für mich ist die Rolle von Behauptungen nicht die Fehlerprüfung an sich (wofür das Testen gedacht ist), sondern sie richten Zäune innerhalb des Codes ein, die bestimmte Fehler nicht durchstehen können. So wird es einfacher, die unvermeidlich auftretenden Fehler aufzuspüren und zu isolieren. Dies sind nur gute Gewohnheiten, die wenig Aufwand erfordern, während das Testen viel Aufwand und viel Zeit erfordert.
Evgeni Sergeev
12

Lesen Sie zuerst die vorhandenen Antworten, dies ist nur ein Nachtrag.

Beachten Sie, dass Sie Ausnahmen mit oder ohne Argumente auslösen können.

Beispiel:

raise SystemExit

Beendet das Programm, aber Sie möchten vielleicht wissen, was passiert ist. Sie können dies also verwenden.

raise SystemExit("program exited")

Dadurch wird "Programm beendet" an stderr ausgegeben, bevor das Programm geschlossen wird.

Anant Prakash
quelle
2
Ist das nicht gegen das OOP-Paradigma? Ich gehe davon aus, dass der erste Fall die Klassenreferenz und der zweite eine Instanz von SystemExit auslöst. Wäre nicht raise SystemExit()die bessere Wahl? Warum funktioniert der erste überhaupt?
Burny
2

Eine andere Möglichkeit, Ausnahmen auszulösen, ist assert. Sie können assert verwenden, um zu überprüfen, ob eine Bedingung erfüllt ist. Andernfalls wird sie ausgelöst AssertionError. Weitere Details finden Sie hier .

def avg(marks):
    assert len(marks) != 0,"List is empty."
    return sum(marks)/len(marks)

mark2 = [55,88,78,90,79]
print("Average of mark2:",avg(mark2))

mark1 = []
print("Average of mark1:",avg(mark1))
Rehan Haider
quelle
2

Nur zur Anmerkung: Es gibt Zeiten, in denen Sie generische Ausnahmen behandeln möchten. Wenn Sie eine Reihe von Dateien verarbeiten und Ihre Fehler protokollieren, möchten Sie möglicherweise alle für eine Datei auftretenden Fehler abfangen, protokollieren und den Rest der Dateien weiter verarbeiten. In diesem Fall a

try:
    foo() 
except Exception as e:
    print(str(e)) # Print out handled error

Blockieren Sie einen guten Weg, um es zu tun. Sie möchten dennoch raisebestimmte Ausnahmen festlegen, damit Sie wissen, was sie bedeuten.

Markemus
quelle
0

Sie sollten dafür die Raise-Anweisung von Python lernen. Es sollte im Try-Block aufbewahrt werden. Beispiel -

try:
    raise TypeError            #remove TypeError by any other error if you want
except TypeError:
    print('TypeError raised')
Abhijeet.py
quelle