without - try except python exceptions




Wie können benutzerdefinierte Ausnahmen im modernen Python deklariert werden? (4)

"Korrekte Möglichkeit benutzerdefinierte Ausnahmen im modernen Python zu deklarieren?"

Dies ist in Ordnung, es sei denn, Ihre Ausnahme ist eine Art spezifischer Ausnahme:

class MyException(Exception):
    pass

Oder besser (vielleicht perfekt), statt pass geben Sie einen Docstring:

class MyException(Exception):
    """Raise for my specific kind of exception"""

Subclassing Exception Subclasses

Aus den docs

Exception

Alle eingebauten, nicht systemauslösenden Ausnahmen werden von dieser Klasse abgeleitet. Alle benutzerdefinierten Ausnahmen sollten auch von dieser Klasse abgeleitet sein.

Das bedeutet, dass, wenn Ihre Ausnahme ein Typ einer spezifischeren Ausnahme ist, diese Ausnahme von der generischen Exception (und das Ergebnis ist, dass Sie immer noch von Exception wie es die Dokumente empfehlen). Außerdem können Sie mindestens einen Docstring bereitstellen (und nicht gezwungen sein, das Schlüsselwort pass zu verwenden):

class MyAppValueError(ValueError):
    '''Raise when my specific value is wrong'''

__init__ Sie Attribute, die Sie selbst erstellen, mit einem benutzerdefinierten __init__ . Vermeiden Sie es, ein Diktat als Positionsargument zu übergeben. Die zukünftigen Benutzer Ihres Codes werden es Ihnen danken. Wenn Sie das veraltete Nachrichtenattribut verwenden, vermeiden Sie selbst, wenn Sie es zuweisen, ein DeprecationWarning :

class MyAppValueError(ValueError):
    '''Raise when a specific subset of values in context of app is wrong'''
    def __init__(self, message, foo, *args):
        self.message = message # without this you may get DeprecationWarning
        # Special attribute you desire with your Error, 
        # perhaps the value that caused the error?:
        self.foo = foo         
        # allow users initialize misc. arguments as any other builtin Error
        super(MyAppValueError, self).__init__(message, foo, *args) 

Es ist wirklich nicht nötig, eigene __str__ oder __repr__ zu schreiben. Die eingebauten sind sehr nett und Ihre kooperative Vererbung stellt sicher, dass Sie sie benutzen.

Kritik der Top-Antwort

Vielleicht habe ich die Frage verpasst, aber warum nicht:

class MyException(Exception):
    pass

Auch hier besteht das Problem darin, dass Sie es entweder spezifisch benennen müssen (wenn es an anderer Stelle erstellt wird) oder Exception abfangen (aber Sie sind wahrscheinlich nicht bereit, alle Arten von Exceptions zu behandeln, und Sie sollten nur Ausnahmen abfangen, die Sie bereit sind zu handhaben). Ähnliche Kritik an der unten, aber das ist nicht die Art und Weise, über super zu initialisieren, und Sie erhalten eine DeprecationWarning wenn Sie auf das Nachrichtenattribut zugreifen:

Bearbeiten: Um etwas zu überschreiben (oder zusätzliche Argumente übergeben), tun Sie dies:

class ValidationError(Exception):
    def __init__(self, message, errors):

        # Call the base class constructor with the parameters it needs
        super(ValidationError, self).__init__(message)

        # Now for your custom code...
        self.errors = errors

Auf diese Weise können Sie dict von Fehlermeldungen an den zweiten Parameter übergeben und später mit e.errors dazu kommen

Es erfordert auch genau zwei Argumente (abgesehen von dem self ). Nicht mehr und nicht weniger. Das ist eine interessante Einschränkung, die zukünftige Benutzer vielleicht nicht zu schätzen wissen.

Um direkt zu sein - es verletzt Liskov Substituierbarkeit .

Ich werde beide Fehler demonstrieren:

>>> ValidationError('foo', 'bar', 'baz').message

Traceback (most recent call last):
  File "<pyshell#10>", line 1, in <module>
    ValidationError('foo', 'bar', 'baz').message
TypeError: __init__() takes exactly 3 arguments (4 given)

>>> ValidationError('foo', 'bar').message
__main__:1: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
'foo'

Verglichen mit:

>>> MyAppValueError('foo', 'FOO', 'bar').message
'foo'

Was ist der richtige Weg, benutzerdefinierte Ausnahmeklassen im modernen Python zu deklarieren? Mein primäres Ziel ist es, dem Standard zu folgen, den andere Ausnahmeklassen haben, so dass (zum Beispiel) jede zusätzliche Zeichenkette, die ich in die Ausnahme einbeziehe, von jedem Tool ausgedruckt wird, das die Ausnahme erfasst hat.

Mit "modernem Python" meine ich etwas, das in Python 2.5 läuft, aber für die Python 2.6- und Python-3-Methode "richtig" ist. Und mit "custom" meine ich ein Exception-Objekt, das zusätzliche Daten über die Fehlerursache enthalten kann: eine Zeichenfolge, vielleicht auch ein anderes beliebiges Objekt, das für die Ausnahme relevant ist.

Ich war über die folgende Verwarnungswarnung in Python 2.6.2 gestolpert:

>>> 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 Attribute namens message . Ich nehme aus PEP-352 dass Attribut in 2.5 eine besondere Bedeutung hat, sie versuchen weg zu verwerfen, also denke ich, dass dieser Name (und dieser alleine) jetzt verboten ist? Pfui.

Ich bin mir auch merklich bewusst, dass Exception einige magische Parameterargumente hat, aber ich habe nie gewusst, wie man es benutzt. Ich bin mir auch nicht sicher, dass es der richtige Weg ist, Dinge voranzutreiben. Viele der Diskussionen, die ich online fand, deuteten darauf hin, dass sie versuchten, in Python 3 die Argumente zu beseitigen.

Update: Zwei Antworten haben vorgeschlagen, __init__ und __str__ / __unicode__ / __repr__ . Das scheint wie viel Tippen, ist es notwendig?


Mit modernen Python-Ausnahmen müssen Sie keine .message missbrauchen oder .__str__() oder .__repr__() oder eine davon überschreiben. Wenn Sie nur eine informative Nachricht erhalten möchten, wenn Ihre Ausnahme ausgelöst wird, führen Sie Folgendes aus:

class MyException(Exception):
    pass

raise MyException("My hovercraft is full of eels")

Das wird eine MyException: My hovercraft is full of eels geben, die mit MyException: My hovercraft is full of eels endet MyException: My hovercraft is full of eels .

Wenn Sie mehr Flexibilität wünschen, können Sie ein Wörterbuch als Argument übergeben:

raise MyException({"message":"My hovercraft is full of animals", "animal":"eels"})

Allerdings ist es etwas komplizierter, in einem except auf diese Details zuzugreifen. Sie werden im Attribut args gespeichert, das eine Liste ist. Sie müssten so etwas tun:

try:
    raise MyException({"message":"My hovercraft is full of animals", "animal":"eels"})
except MyException as e:
    details = e.args[0]
    print(details["animal"])

Es ist immer noch möglich, mehrere Elemente an die Ausnahme zu übergeben, dies wird jedoch in der Zukunft nicht weiter unterstützt. Wenn Sie mehr als nur eine einzige Information benötigen, sollten Sie Exception vollständig unterklassifizieren.


Sehen Sie, wie Ausnahmen standardmäßig funktionieren, wenn ein oder mehrere Attribute verwendet werden (Rückverfolgung entfällt):

>>> raise Exception('bad thing happened')
Exception: bad thing happened

>>> raise Exception('bad thing happened', 'code is broken')
Exception: ('bad thing happened', 'code is broken')

Es könnte also sein, dass Sie eine Art " Ausnahmetemplate " haben möchten, die als Ausnahme selbst kompatibel ist:

>>> nastyerr = NastyError('bad thing happened')
>>> raise nastyerr
NastyError: bad thing happened

>>> raise nastyerr()
NastyError: bad thing happened

>>> raise nastyerr('code is broken')
NastyError: ('bad thing happened', 'code is broken')

Dies kann leicht mit dieser Unterklasse durchgeführt werden

class ExceptionTemplate(Exception):
    def __call__(self, *args):
        return self.__class__(*(self.args + args))
# ...
class NastyError(ExceptionTemplate): pass

Wenn Ihnen diese standardmäßige __str__ Darstellung nicht gefällt, fügen __str__ der ExceptionTemplate Klasse einfach die __str__ Methode hinzu:

    # ...
    def __str__(self):
        return ': '.join(self.args)

und du wirst haben

>>> raise nastyerr('code is broken')
NastyError: bad thing happened: code is broken

Sie sollten die Methoden __repr__ oder __unicode__ , statt die Nachricht zu verwenden. Die __unicode__ , die Sie beim __unicode__ der Ausnahme __repr__ __unicode__ sich im Attribut args des Ausnahmeobjekts.





exception