Python "erhöhen von" Verwendung

196

Was ist der Unterschied zwischen raiseund raise fromin Python?

try:
    raise ValueError
except Exception as e:
    raise IndexError

was ergibt

Traceback (most recent call last):
  File "tmp.py", line 2, in <module>
    raise ValueError
ValueError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "tmp.py", line 4, in <module>
    raise IndexError
IndexError

und

try:
    raise ValueError
except Exception as e:
    raise IndexError from e

was ergibt

Traceback (most recent call last):
  File "tmp.py", line 2, in <module>
    raise ValueError
ValueError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "tmp.py", line 4, in <module>
    raise IndexError from e
IndexError
Darkfeline
quelle
9
Haben Sie PEP-3134 gelesen ?
Jonrsharpe
4
Verwenden Sie jetzt raise IndexError from Nonesagen wir.
Martijn Pieters
11
Heh. raise IndexError from Falsewirft ein TypeError, nicht ein IndexError. Meinen Tag gerettet.
Mad Physicist
Ich bin mir nicht sicher, ob dies der richtige Ort ist, um es zu erwähnen, aber für jeden, der Spyder verwendet: Dieses gesamte Konstrukt funktioniert dort nicht. Es ist seit über 3 Jahren ein Problem ( github.com/spyder-ide/spyder/issues/2943 ), aber sie scheinen zu glauben, dass keine Notwendigkeit für verkettete Ausnahmen besteht.
Emil Bode

Antworten:

227

Der Unterschied besteht darin, dass bei Verwendung fromdas __cause__Attribut festgelegt wird und die Nachricht angibt, dass die Ausnahme direkt durch verursacht wurde . Wenn Sie das weglassen, fromwird no __cause__gesetzt, aber das __context__Attribut kann auch gesetzt werden, und der Traceback zeigt dann den Kontext an, als ob während der Behandlung etwas anderes passiert wäre .

Das Festlegen des __context__Ereignisses erfolgt, wenn Sie es raisein einem Ausnahmebehandler verwendet haben. Wenn Sie raiseirgendwo anders verwendet haben, ist auch nein __context__eingestellt.

Wenn a __cause__gesetzt ist, __suppress_context__ = Truewird auch für die Ausnahme ein Flag gesetzt. Wenn auf __suppress_context__gesetzt, Truewird das __context__beim Drucken eines Tracebacks ignoriert.

Wenn man von einem Exception - Handler heben, das Sie nicht wollen , um den Kontext zeigen (nicht will , eine andere Ausnahme geschah während der Handhabung Nachricht), dann verwenden , raise ... from Noneum Satz __suppress_context__zu True.

Mit anderen Worten, Python legt einen Kontext für Ausnahmen fest, damit Sie überprüfen können, wo eine Ausnahme ausgelöst wurde, und sehen können, ob eine andere Ausnahme durch diese ersetzt wurde. Sie können einer Ausnahme auch eine Ursache hinzufügen , indem Sie den Traceback für die andere Ausnahme explizit machen (andere Formulierung verwenden), und der Kontext wird ignoriert (kann jedoch beim Debuggen weiterhin überprüft werden). Mit raise ... from Nonekönnen Sie den zu druckenden Kontext unterdrücken.

Siehe die raiseErklärung Dokumentation :

Die fromKlausel wird für die Verkettung von Ausnahmen verwendet: Wenn angegeben, muss der zweite Ausdruck eine andere Ausnahmeklasse oder -instanz sein, die dann als __cause__Attribut (das beschreibbar ist) an die ausgelöste Ausnahme angehängt wird. Wenn die ausgelöste Ausnahme nicht behandelt wird, werden beide Ausnahmen gedruckt:

>>> try:
...     print(1 / 0)
... except Exception as exc:
...     raise RuntimeError("Something bad happened") from exc
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: int division or modulo by zero

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened

Ein ähnlicher Mechanismus funktioniert implizit, wenn eine Ausnahme in einem Ausnahmehandler oder einer finallyKlausel ausgelöst wird: Die vorherige Ausnahme wird dann als __context__Attribut der neuen Ausnahme angehängt :

>>> try:
...     print(1 / 0)
... except:
...     raise RuntimeError("Something bad happened")
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: int division or modulo by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened

Siehe auch die eingebauten Ausnahmen Dokumentation für weitere Einzelheiten über den Kontext und Ursache Informationen zu Ausnahmen angebracht.

Martijn Pieters
quelle
11
Gibt es einen Grund, die Ausnahmen explizit mit fromund __cause__anstelle des impliziten zu verketten __context__? Gibt es Fälle, in denen man eine andere Ausnahme anhängen würde als die, die von der gefangen wurde except?
Darkfeline
13
@darkfeline: Nehmen wir an, Ihre Datenbank-API unterstützt das Öffnen von Datenbanken aus verschiedenen Quellen, einschließlich Web und Festplatte. Ihre API löst immer ein aus, DatabaseErrorwenn das Öffnen der Datenbank fehlschlägt. Wenn der Fehler jedoch darauf zurückzuführen ist, IOErrordass eine Datei nicht geöffnet werden konnte oder eine HTTPErrorURL nicht funktioniert hat, möchten Sie diesen Kontext explizit einschließen, damit der Entwickler, der die API verwendet, debuggen kann, warum dies so ist. In diesem Moment verwenden Sie raise DatabaseError from original_exception.
Martijn Pieters
4
@darkfeline: Wenn dieser Entwickler die Verwendung der Datenbank-API in seine eigene API einwickelt und diese IOErroroder HTTPErroran seine Verbraucher weitergeben möchte, müsste er diese verwenden raise NewException from databaseexception.__cause__und jetzt eine andere Ausnahme als die verwendenDatabaseException , die sie gerade abgefangen haben.
Martijn Pieters
2
@ dan3: nein, gibt es nicht. Ausnahmeverkettung ist eine reine Python 3-Funktion.
Martijn Pieters
5
@ laike9m: du meinst, wenn du eine Ausnahme behandelst foound eine neue Ausnahme auslösen willst bar? Dann können Sie raise bar from fooPython-Status verwenden und haben, der foo direkt verursachtbar . Wenn Sie nicht verwenden from foo, dann druckt Python noch beide, aber fest , dass während der Handhabung foo, barangehoben wurde , andere Botschaft, sollte einen möglichen Fehler in der Fehlerbehandlung Flagge.
Martijn Pieters