444 Stimmen

Python "raise from" Verwendung

Was ist der Unterschied zwischen raise und raise from in Python?

try:
    raise ValueError
except Exception as e:
    raise IndexError

was ergibt

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

Während der Behandlung der obigen Ausnahme trat eine weitere Ausnahme auf:

Traceback (most recent call last):
  File "tmp.py", line 4, in 
    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 
    raise ValueError
ValueError

Die obige Ausnahme war die direkte Ursache der folgenden Ausnahme:

Traceback (most recent call last):
  File "tmp.py", line 4, in 
    raise IndexError from e
IndexError

492voto

Martijn Pieters Punkte 953257

Der Unterschied besteht darin, dass wenn Sie from verwenden, das __cause__ Attribut gesetzt wird und die Meldung besagt, dass die Ausnahme direkt durch verursacht wurde. Wenn Sie das from auslassen, wird kein __cause__ festgelegt, aber das __context__ Attribut kann ebenfalls festgelegt sein und der Traceback zeigt dann den Kontext als bei der Bearbeitung eines anderen Fehlers ist etwas passiert.

Das Setzen des __context__ passiert, wenn Sie raise in einem Ausnahmehandler verwendet haben; wenn Sie raise an anderer Stelle verwenden, wird auch kein __context__ festgelegt.

Wenn ein __cause__ festgelegt ist, wird auch ein __suppress_context__ = True Flag auf der Ausnahme festgelegt; wenn __suppress_context__ auf True gesetzt ist, wird der __context__ ignoriert, wenn ein Traceback gedruckt wird.

Beim Werfen aus einem Ausnahmebehandler heraus, in dem Sie den Kontext nicht anzeigen möchten (keine bei der Bearbeitung einer anderen Ausnahme passierte Meldung), verwenden Sie raise ... from None, um __suppress_context__ auf True zu setzen.

Mit anderen Worten, Python setzt einen Kontext bei Ausnahmen, damit Sie nachvollziehen können, wo eine Ausnahme ausgelöst wurde, und können sehen, ob eine andere Ausnahme durch sie ersetzt wurde. Sie können auch eine Ursache zu einer Ausnahme hinzufügen, um den Traceback explizit über die andere Ausnahme zu informieren (verwenden Sie andere Wörter), und der Kontext wird ignoriert (kann jedoch beim Debuggen weiterhin untersucht werden). Mit raise ... from None können Sie verhindern, dass der Kontext gedruckt wird.

Siehe die raise Anweisungsdokumentation:

Die from Klausel wird für die Verkettung von Ausnahmen verwendet: Wenn sie angegeben wird, muss der zweite Ausdruck eine andere Ausnahmeklasse oder Instanz sein, die dann der ausgelösten Ausnahme als das __cause__ Attribut (das beschreibbar ist) 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("Etwas Schlechtes ist passiert") from exc
...
Traceback (most recent call last):
  File "", line 2, in 
ZeroDivisionError: int division or modulo by zero

Die obige Ausnahme war die direkte Ursache der folgenden Ausnahme:

Traceback (most recent call last):
  File "", line 4, in 
RuntimeError: Etwas Schlechtes ist passiert

Ein ähnlicher Mechanismus funktioniert implizit, wenn eine Ausnahme innerhalb eines Ausnahmehandlers oder einer finally-Klausel ausgelöst wird: die vorherige Ausnahme wird dann als das __context__ Attribut der neuen Ausnahme angehängt:

>>> try:
...     print(1 / 0)
... except:
...     raise RuntimeError("Etwas Schlechtes ist passiert")
...
Traceback (most recent call last):
  File "", line 2, in 
ZeroDivisionError: int division or modulo by zero

Bei der Behandlung der obigen Ausnahme ist eine weitere Ausnahme aufgetreten:

Traceback (most recent call last):
  File "", line 4, in 
RuntimeError: Etwas Schlechtes ist passiert

Siehe auch die Dokumentation zu den integrierten Ausnahmen für Details zu den am Ausnahmen angehängten Kontext- und Ursacheninformationen.

20voto

Maggyero Punkte 4419

Im Jahr 2005 führte PEP 3134, Exception-Chaining und eingebettete Tracebacks die Exception-Chaining ein:

  • implizites Chaining mit explizitem raise EXCEPTION oder implizitem Raise (__context__ Attribut);
  • explizites Chaining mit explizitem raise EXCEPTION from CAUSE (__cause__ Attribut).

Motivation

Während der Behandlung einer Ausnahme (Ausnahme A) besteht die Möglichkeit, dass eine weitere Ausnahme (Ausnahme B) auftritt. In der heutigen Python-Version 2.4 wird, wenn dies geschieht, Ausnahme B nach außen weitergeleitet und Ausnahme A geht verloren. Um das Problem zu debuggen, ist es nützlich, über beide Ausnahmen Bescheid zu wissen. Das __context__ Attribut behält diese Informationen automatisch bei.

Manchmal kann es für einen Ausnahme-Handler nützlich sein, eine Ausnahme absichtlich erneut auszulösen, entweder um zusätzliche Informationen bereitzustellen oder um eine Ausnahme in einen anderen Typ zu übersetzen. Das __cause__ Attribut bietet einen expliziten Weg, um die direkte Ursache einer Ausnahme aufzuzeichnen.

[...]

Implizites Exception-Chaining

Hier ist ein Beispiel, das das __context__ Attribut verdeutlicht:

def compute(a, b):
    try:
        a/b
    except Exception, exc:
        log(exc)

def log(exc):
    file = open('logfile.txt')  # oh, vergessen das 'w'
    print >>file, exc
    file.close()

Der Aufruf von compute(0, 0) löst einen ZeroDivisionError aus. Die compute() Funktion fängt diese Ausnahme ab und ruft log(exc) auf, aber die log() Funktion löst ebenfalls eine Ausnahme aus, wenn sie versucht, in eine Datei zu schreiben, die nicht zum Schreiben geöffnet wurde.

In der heutigen Python-Version erhält der Aufrufer von compute() einen IOError. Der ZeroDivisionError geht verloren. Mit der vorgeschlagenen Änderung hat die Instanz von IOError ein zusätzliches __context__ Attribut, das den ZeroDivisionError speichert.

[…]

Explizites Exception-Chaining

Das __cause__ Attribut von Ausnahme-Objekten wird immer auf None initialisiert. Es wird durch eine neue Form des raise Statements gesetzt:

raise EXCEPTION from CAUSE

was äquivalent ist zu:

exc = EXCEPTION
exc.__cause__ = CAUSE
raise exc

In folgendem Beispiel stellt eine Datenbank Implementierungen für verschiedene Arten von Speicher bereit, wobei Dateispeicher eine Art davon ist. Der Datenbank-Designer möchte, dass Fehler als DatabaseError Objekte weitergeleitet werden, damit der Klient nicht über die details des spezifischen Speichers informiert werden muss, aber möchte die zugrunde liegenden Fehlerinformationen nicht verlieren.

class DatabaseError(Exce...

5voto

y.selivonchyk Punkte 7720

Die kürzeste Antwort. PEP-3134 sagt alles aus. raise Exception from e setzt das __cause__ Feld der neuen Ausnahme.

Eine längere Antwort aus demselben PEP:

  • __context__ Feld würde implizit auf den ursprünglichen Fehler innerhalb des except: Blocks gesetzt werden, es sei denn, es wird mit __suppress_context__ = True nicht unterdrückt.
  • __cause__ ist wie context, muss aber explizit mit der from Syntax gesetzt werden
  • traceback wird immer auftreten, wenn Sie raise innerhalb eines except Blocks aufrufen. Sie können das Traceback beseitigen, indem Sie a) eine Ausnahme schlucken except: pass oder indem Sie direkt mit sys.exc_info() herumspielen.

Die lange Antwort

import traceback 
import sys

class CustomError(Exception):
    def __init__(self):
        super().__init__("custom")

def print_exception(func):
    print(f"\n\n\nFUNKTION '{func.__name__}' AUSFÜHREN \n")
    try:
        func()
    except Exception as e:
        "Hier ist das Ergebnis unserer Aktionen:"
        print(f"\tFehlertyp:    '{type(e)}'")
        print(f"\tFehlermeldung: '{e}'")
        print(f"\tFehlerkontext: '{e.__context__}'")
        print(f"\tKontexttyp:      '{type(e.__context__)}'")
        print(f"\tFehlerursache:   '{e.__cause__}'")
        print(f"\tUrsachentyp:         '{type(e.__cause__)}'")
        print("\nTRACESTART>>>")
        traceback.print_exc()
        print("<<

`

Wird die folgende Ausgabe ergeben:

Hinweis: Dies wird nichts ausgeben
FUNKTION 'vanilla_catch_swallow' AUSFÜHREN 

Hinweis: Dies wird AttributeError und 1 Stack-Trace ausgeben
FUNKTION 'vanilla_catch_reraise' 

        Fehlertyp:    ''
        Fehlermeldung: ''dict' object has no attribute 'does_not_exist''
        Fehlerkontext: 'None'
        Kontexttyp:      ''
        Fehlerursache:   'None'
        Ursachentyp:         ''

TRACESTART>>>
Traceback (most recent call last):
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
    func()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 41, in vanilla_catch_reraise
    raise e
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 39, in vanilla_catch_reraise
    original_error_emitter()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 27, in original_error_emitter
    print(x.does_not_exist)
AttributeError: 'dict' object has no attribute 'does_not_exist'
<<'
        Fehlermeldung: 'custom'
        Fehlerkontext: ''dict' object has no attribute 'does_not_exist''
        Kontexttyp:      ''
        Fehlerursache:   'None'
        Ursachentyp:         ''

TRACESTART>>>
Traceback (most recent call last):
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 46, in catch_replace
    original_error_emitter()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 27, in original_error_emitter
    print(x.does_not_exist)
AttributeError: 'dict' object has no attribute 'does_not_exist'

Während der Behandlung der obigen Ausnahme trat eine weitere Ausnahme auf:

Traceback (most recent call last):
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
    func()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 48, in catch_replace
    raise CustomError()
CustomError: custom
<<'
        Fehlermeldung: 'custom'
        Fehlerkontext: ''dict' object has no attribute 'does_not_exist''
        Kontexttyp:      ''
        Fehlerursache:   ''dict' object has no attribute 'does_not_exist''
        Ursachentyp:         ''

TRACESTART>>>
Traceback (most recent call last):
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 53, in catch_replace_with_from
    original_error_emitter()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 27, in original_error_emitter
    print(x.does_not_exist)
AttributeError: 'dict' object has no attribute 'does_not_exist'

Die obige Ausnahme war die direkte Ursache der folgenden Ausnahme:

Traceback (most recent call last):
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
    func()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 55, in catch_replace_with_from
    raise CustomError() from e
CustomError: custom
<<'
        Fehlermeldung: 'custom'
        Fehlerkontext: 'None'
        Kontexttyp:      ''
        Fehlerursache:   'None'
        Ursachentyp:         ''

TRACESTART>>>
Traceback (most recent call last):
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
    func()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 64, in catch_reset_trace
    raise CustomError()
CustomError: custom
<<

`

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X