exception try - Wie können benutzerdefinierte Ausnahmen im modernen Python deklariert werden?




caught getting (6)

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?


Answers

Nein, "Nachricht" ist nicht verboten. Es ist nur veraltet. Ihre Anwendung wird mit der Verwendung der Nachricht funktionieren. Vielleicht möchten Sie den Deprecation-Fehler natürlich loswerden.

Wenn Sie benutzerdefinierte Ausnahmeklassen für Ihre Anwendung erstellen, werden viele von ihnen nicht nur von Exception abgeleitet, sondern von anderen, wie ValueError oder ähnlichen. Dann müssen Sie sich an ihre Verwendung von Variablen anpassen.

Und wenn Sie in Ihrer Anwendung viele Ausnahmen haben, ist es normalerweise eine gute Idee, für alle eine gemeinsame benutzerdefinierte Basisklasse zu haben, damit die Benutzer Ihrer Module dies tun können

try:
    ...
except NelsonsExceptions:
    ...

Und in diesem Fall können Sie die benötigten __init__ and __str__ dort machen, so dass Sie sie nicht für jede Ausnahme wiederholen müssen. Aber einfach die Nachrichtenvariable etwas anderes als die Nachricht aufrufen, macht den Trick.

In jedem Fall brauchen Sie nur __init__ or __str__ wenn Sie etwas anderes als die Exception selbst tun. Und wenn die Depretierung, dann brauchen Sie beide, oder Sie erhalten einen Fehler. Das ist nicht viel extra Code, den man pro Klasse braucht. ;)


"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'

Vielleicht habe ich die Frage verpasst, aber warum nicht:

class MyException(Exception):
    pass

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

Python 3 Update: In Python 3+ können Sie diese etwas kompaktere Verwendung von super() :

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

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

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

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.


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.


Wenn Sie Datei für Zeile lesen und ermitteln möchten, welche Zeile leer ist, stellen Sie sicher, dass Sie .strip() , da in der "leeren" Zeile ein neues .strip() steht:

lines = open("my_file.log", "r").readlines()

for line in lines:
    if not line.strip():
        continue

    # your code for non-empty lines




python exception