Was ist der richtige Weg, um benutzerdefinierte Ausnahmeklassen in modernem Python zu deklarieren? Mein primäres Ziel ist es, den Standard anderer Ausnahmeklassen zu befolgen, damit (zum Beispiel) jede zusätzliche Zeichenfolge, die ich in die Ausnahme einbinde, von dem Tool ausgedruckt wird, das die Ausnahme abgefangen hat.
Mit "modernem Python" meine ich etwas, das in Python 2.5 ausgeführt wird, aber für Python 2.6 und Python 3. * korrekt ist. * Vorgehensweise. Und mit "benutzerdefiniert" meine ich ein Ausnahmeobjekt, das zusätzliche Daten zur Fehlerursache enthalten kann: eine Zeichenfolge, möglicherweise auch ein anderes für die Ausnahme relevantes beliebiges Objekt.
Ich wurde durch die folgende Verfallswarnung in Python 2.6.2 ausgelöst:
>>> class MyError(Exception):
... def __init__(self, message):
... self.message = message
...
>>> MyError("foo")
_sandbox.py:3: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
Es scheint verrückt, dass BaseException
eine besondere Bedeutung für die genannten Attribute hat message
. Ich versammle mich aus PEP-352 dass dieses Attribut in 2.5 eine besondere Bedeutung hatte. Sie versuchen, es zu verwerfen. Ich denke also, dass dieser Name (und dieser allein) jetzt verboten ist. Pfui.
Ich bin mir auch unscharf bewusst, dass Exception
es einige magische Parameter gibt args
, aber ich habe nie gewusst, wie man sie benutzt. Ich bin mir auch nicht sicher, ob dies der richtige Weg ist, um die Dinge in Zukunft zu erledigen. Viele der Diskussionen, die ich online fand, deuteten darauf hin, dass sie versuchten, Argumente in Python 3 zu beseitigen.
Update: Zwei Antworten haben vorgeschlagen, überschrieben zu werden __init__
, und __str__
/ __unicode__
/ __repr__
. Das scheint viel zu tippen, ist es notwendig?
quelle
Mit moderner Python Ausnahmen, brauchen Sie nicht , um Missbrauch
.message
oder Überschreibung.__str__()
oder.__repr__()
oder irgendetwas davon. Wenn Sie beim Auslösen Ihrer Ausnahme nur eine informative Nachricht wünschen, gehen Sie wie folgt vor:Das gibt einen Traceback, der mit endet
MyException: My hovercraft is full of eels
.Wenn Sie mehr Flexibilität für die Ausnahme wünschen, können Sie ein Wörterbuch als Argument übergeben:
Es ist jedoch
except
etwas komplizierter, diese Details in einem Block zu erfassen. Die Details werden imargs
Attribut gespeichert , das eine Liste ist. Sie müssten so etwas tun:Es ist weiterhin möglich, mehrere Elemente an die Ausnahme zu übergeben und über Tupelindizes darauf zuzugreifen. Dies wird jedoch dringend empfohlen (und war vor einiger Zeit sogar für die Verwerfung vorgesehen). Wenn Sie mehr als eine einzelne Information benötigen und die oben beschriebene Methode für Sie nicht ausreicht, sollten Sie
Exception
die im Lernprogramm beschriebene Unterklasse verwenden .quelle
Exception(foo, bar, qux)
.Dies ist in Ordnung, es sei denn, Ihre Ausnahme ist wirklich eine Art spezifischere Ausnahme:
Oder besser (vielleicht perfekt), anstatt
pass
einen Docstring zu geben:Unterklassen Ausnahme Unterklassen
Aus den Dokumenten
Das heißt, wenn Ihre Ausnahme ein Typ einer spezifischeren Ausnahme ist, klassifizieren Sie diese Ausnahme anstelle der generischen
Exception
(und das Ergebnis ist, dass Sie immer noch davon ableiten,Exception
wie in den Dokumenten empfohlen). Sie können auch mindestens eine Dokumentzeichenfolge angeben (und nicht gezwungen sein, daspass
Schlüsselwort zu verwenden):Legen Sie Attribute fest, die Sie selbst mit einem benutzerdefinierten erstellen
__init__
. Vermeiden Sie es, ein Diktat als Positionsargument zu übergeben. Zukünftige Benutzer Ihres Codes werden es Ihnen danken. Wenn Sie das veraltete Nachrichtenattribut verwenden, wird durch die Zuweisung selbst Folgendes vermiedenDeprecationWarning
:Es ist wirklich nicht nötig, eigene
__str__
oder zu schreiben__repr__
. Die eingebauten sind sehr nett und Ihre kooperative Vererbung stellt sicher, dass Sie sie verwenden.Kritik der Top-Antwort
Wiederum besteht das Problem mit dem oben Gesagten darin, dass Sie es entweder spezifisch benennen müssen (importieren, wenn es an anderer Stelle erstellt wurde) oder Ausnahme abfangen müssen, um es abzufangen (aber Sie sind wahrscheinlich nicht bereit, alle Arten von Ausnahmen zu behandeln). und Sie sollten nur Ausnahmen abfangen, für die Sie bereit sind). Ähnliche Kritik wie unten, aber zusätzlich ist dies nicht die Möglichkeit, über zu initialisieren
super
, und Sie erhalten eine,DeprecationWarning
wenn Sie auf das Nachrichtenattribut zugreifen:Außerdem müssen genau zwei Argumente (abgesehen von
self
) übergeben werden. Nicht mehr und nicht weniger. Dies ist eine interessante Einschränkung, die zukünftige Benutzer möglicherweise nicht zu schätzen wissen.Direkt zu sein - es verletzt die Substituierbarkeit von Liskov .
Ich werde beide Fehler demonstrieren:
Verglichen mit:
quelle
BaseException.message
ist in Python 3 weg, also gilt die Kritik nur für alte Versionen, oder?__str__
Methode neu definieren ,MyAppValueError
anstatt sich auf dasmessage
Attribut zu verlassenValueError
. Dies ist sinnvoll, wenn es sich um die Kategorie der Wertfehler handelt. Wenn es nicht in der Kategorie der Wertfehler liegt, würde ich in der Semantik dagegen argumentieren. Es gibt Raum für einige Nuancen und Überlegungen seitens des Programmierers, aber ich bevorzuge die Spezifität, wenn zutreffend. Ich werde meine Antwort aktualisieren, um das Thema bald besser anzugehen.sehen , wie Ausnahmen standardmäßig arbeiten , wenn man gegen mehrere Attribute verwendet werden (Tracebacks weggelassen):
Daher möchten Sie möglicherweise eine Art " Ausnahmevorlage " haben, die auf kompatible Weise als Ausnahme selbst fungiert:
Dies kann mit dieser Unterklasse problemlos durchgeführt werden
und wenn Ihnen diese standardmäßige tupelartige Darstellung nicht gefällt, fügen Sie
__str__
derExceptionTemplate
Klasse einfach eine Methode hinzu , wie:und du wirst haben
quelle
Ab Python 3.8 (2018, https://docs.python.org/dev/whatsnew/3.8.html ) lautet die empfohlene Methode weiterhin:
Bitte vergessen Sie nicht zu dokumentieren, warum eine benutzerdefinierte Ausnahme erforderlich ist!
Wenn nötig, ist dies der richtige Weg, um Ausnahmen mit mehr Daten zu vermeiden:
und holen sie wie:
payload=None
ist wichtig, damit es beizbar ist. Bevor Sie es ausgeben, müssen Sie anrufenerror.__reduce__()
. Das Laden funktioniert wie erwartet.Möglicherweise sollten Sie bei der Suche nach einer Lösung mithilfe der Python-
return
Anweisung nachforschen, ob viele Daten für die Übertragung in eine äußere Struktur erforderlich sind. Dies scheint mir klarer / pythonischer zu sein. Erweiterte Ausnahmen werden in Java häufig verwendet, was manchmal ärgerlich sein kann, wenn ein Framework verwendet wird und alle möglichen Fehler abgefangen werden müssen.quelle
__str__
) und nicht andere Antworten, die verwendet werdensuper().__init__(...)
. Nur eine Schande, die für eine bessere "Standard" -Serialisierung überschreibt__str__
und__repr__
wahrscheinlich notwendig ist.Sie sollten
__repr__
oder überschreiben__unicode__
, anstatt die Nachricht zu verwenden. Die Argumente, die Sie beim Erstellen der Ausnahme angeben, befinden sich imargs
Attribut des Ausnahmeobjekts.quelle
Nein, "Nachricht" ist nicht verboten. Es ist nur veraltet. Ihre Anwendung funktioniert gut mit der Verwendung von Nachrichten. Aber vielleicht möchten Sie den Verfallsfehler natürlich beseitigen.
Wenn Sie benutzerdefinierte Ausnahmeklassen für Ihre Anwendung erstellen, werden viele von ihnen nicht nur von Exception, sondern auch von anderen wie ValueError oder ähnlichem unterklassifiziert. Dann müssen Sie sich an die Verwendung von Variablen anpassen.
Und wenn Ihre Anwendung viele Ausnahmen enthält, ist es normalerweise eine gute Idee, eine gemeinsame benutzerdefinierte Basisklasse für alle zu haben, damit Benutzer Ihrer Module dies tun können
Und in diesem Fall können Sie das tun
__init__ and __str__
Notwendige dort , sodass Sie es nicht für jede Ausnahme wiederholen müssen. Aber einfach die Nachrichtenvariable etwas anderes als Nachricht aufzurufen, reicht aus.In jedem Fall benötigen Sie das nur,
__init__ or __str__
wenn Sie etwas anderes tun als Exception selbst. Und denn wenn die Abwertung, dann brauchen Sie beide, oder Sie bekommen eine Fehlermeldung. Das ist nicht viel zusätzlicher Code, den Sie pro Klasse benötigen. ;)quelle
Siehe einen sehr guten Artikel " Die endgültige Anleitung zu Python-Ausnahmen ". Die Grundprinzipien sind:
BaseException.__init__
mit nur einem Argument an.Es gibt auch Informationen zum Organisieren (in Modulen) und zum Umschließen von Ausnahmen. Ich empfehle, die Anleitung zu lesen.
quelle
Always call BaseException.__init__ with only one argument.
Scheint eine nicht benötigte Einschränkung zu sein, da tatsächlich eine beliebige Anzahl von Argumenten akzeptiert wird .Versuchen Sie dieses Beispiel
quelle
Um Ihre eigenen Ausnahmen richtig zu definieren, müssen Sie einige bewährte Methoden befolgen:
Definieren Sie eine Basisklasse, von der geerbt wird
Exception
. Auf diese Weise können Ausnahmen im Zusammenhang mit dem Projekt abgefangen werden (spezifischere Ausnahmen sollten davon erben):Das Organisieren dieser Ausnahmeklassen in einem separaten Modul (z. B.
exceptions.py
) ist im Allgemeinen eine gute Idee.Unterklassifizieren Sie die Basisausnahmeklasse, um eine benutzerdefinierte Ausnahme zu erstellen.
Definieren Sie eine benutzerdefinierte
__init__()
Methode mit einer variablen Anzahl von Argumenten , um einer benutzerdefinierten Ausnahme zusätzliche Argumente hinzuzufügen . Rufen Sie die Basisklasse auf__init__()
und übergeben Sie ihr Positionsargumente (denken Sie daranBaseException
/Exception
erwarten Sie eine beliebige Anzahl von Positionsargumenten ):Um eine solche Ausnahme mit einem zusätzlichen Argument auszulösen, können Sie Folgendes verwenden:
Dieses Design entspricht dem Liskov-Substitutionsprinzip , da Sie eine Instanz einer Basisausnahmeklasse durch eine Instanz einer abgeleiteten Ausnahmeklasse ersetzen können. Außerdem können Sie eine Instanz einer abgeleiteten Klasse mit denselben Parametern wie die übergeordnete Klasse erstellen.
quelle