BaseException.message ist in Python 2.6 veraltet

176

Ich erhalte eine Warnung, dass BaseException.message in Python 2.6 veraltet ist, wenn ich die folgende benutzerdefinierte Ausnahme verwende:

class MyException(Exception):

    def __init__(self, message):
        self.message = message

    def __str__(self):
        return repr(self.message)

Dies ist die Warnung:

DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
self.message = message

Was ist daran falsch? Was muss ich ändern, um die Abwertungswarnung zu entfernen?

trostlos
quelle
9
Siehe PEP 352 für die Gründe: python.org/dev/peps/pep-0352/#retracted-ideas
balpha

Antworten:

154

Lösung - fast keine Codierung erforderlich

Erben Sie einfach Ihre Ausnahmeklasse von Exceptionund übergeben Sie die Nachricht als ersten Parameter an den Konstruktor

Beispiel:

class MyException(Exception):
    """My documentation"""

try:
    raise MyException('my detailed description')
except MyException as my:
    print my # outputs 'my detailed description'

Sie können str(my)oder (weniger elegant) verwenden, my.args[0]um auf die benutzerdefinierte Nachricht zuzugreifen.

Hintergrund

In den neueren Versionen von Python (ab 2.6) sollen wir unsere benutzerdefinierten Ausnahmeklassen von Exception erben, die ( ab Python 2.5 ) von BaseException erben. Der Hintergrund ist in PEP 352 ausführlich beschrieben .

class BaseException(object):

    """Superclass representing the base of the exception hierarchy.
    Provides an 'args' attribute that contains all arguments passed
    to the constructor.  Suggested practice, though, is that only a
    single string argument be passed to the constructor."""

__str__und __repr__sind bereits sinnvoll implementiert, insbesondere für den Fall nur eines Arguments (das als Nachricht verwendet werden kann).

Sie müssen nicht wiederholen __str__oder __init__implementieren oder erstellen, _get_messagewie von anderen vorgeschlagen.

geekQ
quelle
5
BaseException in Exception geändert, wie von @Matt
geekQ vorgeschlagen
8
Verwenden von strPausen, wenn die Ausnahme mit einem Unicode-Argument erstellt wurde: str(MyException(u'\xe5'))Raises UnicodeEncodeError. Die Verwendung von unicodeanstelle von strist auch nicht kinderleicht, da unicode(MyException('\xe5'))UnicodeDecodeError ausgelöst wird. Bedeutet dies, dass ich, wenn ich nicht im Voraus weiß, ob das Argument stroder ist unicode, verwenden muss, .args[0]wo ich es zuvor verwendet habe .message?
Kasperd
1
@kasperd Wie praktisch alle Python-Unicode-Probleme kann dies mit einem Unicode-Sandwich gelöst werden .
Ryan P
3
@ RyanP Das setzt voraus, dass ich tatsächlich die Kontrolle darüber habe, was rein geht. Hier ist eine Tatsache des Lebens, mit der ich konfrontiert war. Ich muss Ausnahmen von mehreren Bibliotheken von Drittanbietern behandeln. Einige von ihnen übergeben Unicode an ihre Ausnahmen und andere übergeben str. Eine der Bibliotheken hat sogar eine eigene Klasse, die von Unicode erbt, aber eine eigene repr-Methode, die Unicode anstelle von str zurückgibt, wie es die Spezifikation erfordert.
Kasperd
1
Ich habe es übrigens getan. Ersetzen Sie jede Verwendung von .messagein unserem Projekt durch .args[0], und das hat uns keine Probleme bereitet.
Kasperd
26

Ja, es ist in Python 2.6 veraltet, weil es in Python 3.0 nicht mehr funktioniert

Die BaseException-Klasse bietet keine Möglichkeit mehr, Fehlermeldungen zu speichern. Sie müssen es selbst implementieren. Sie können dies mit einer Unterklasse tun, die eine Eigenschaft zum Speichern der Nachricht verwendet.

class MyException(Exception):
    def _get_message(self): 
        return self._message
    def _set_message(self, message): 
        self._message = message
    message = property(_get_message, _set_message)

Hoffe das hilft

Sahas
quelle
1
Wie würden Sie die Nachricht während der Erhöhung initialisieren? Sein Code zeigte die Nachricht, die durch Aufrufen von MyException ("irgendeine Nachricht") gesetzt wurde
eric.frederich
Die Methoden in meinem Beispiel dienen nur zum Implementieren der Nachrichteneigenschaft. Wie die Eigenschaft verwendet wird, hängt vom Codierer ab. In diesem Fall verwendet OP die Methoden init und str , die er in seinem Code veröffentlicht hat.
Sahas
1
Verwenden Sie eine öffentliche Variable anstelle eines Getters / Setters, wenn nur eine andere Variable gelesen wird. Sie können dies später jederzeit auf eine @propertySyntax aktualisieren, wenn Sie wirklich eine Kapselung benötigen.
Vdboor
2
@vdboor: Er verwendet @property, um die Verfallswarnung zu deaktivieren.
Bukzor
Es ist unpythonisch und nutzlos, Eigenschaften zu erstellen, die nichts anderes tun, als den Wert abzurufen und festzulegen, ungeprüft und unverändert. Der springende Punkt bei Eigenschaften ist, dass man öffentliche Attribute frei verwenden kann, bis tatsächlich ein Getter oder ein Setter benötigt wird. Anschließend verwandeln Sie das öffentliche Attribut in eine Eigenschaft, ohne den Clientcode zu beschädigen.
Luciano Ramalho
9
class MyException(Exception):

    def __str__(self):
        return repr(self.args[0])

e = MyException('asdf')
print e

Dies ist Ihre Klasse im Python2.6-Stil. Die neue Ausnahme akzeptiert eine beliebige Anzahl von Argumenten.

Maxim Sloyko
quelle
1
Die alte Exception-Klasse akzeptiert auch eine beliebige Anzahl von Argumenten. Sie können die Nachrichteneigenschaft wie das, was Sie tun, vollständig vermeiden. Wenn dies jedoch Ihren vorhandenen Code beschädigen würde, können Sie das Problem lösen, indem Sie Ihre eigene Nachrichteneigenschaft implementieren.
Sahas
8

So replizieren Sie die Warnung

Lassen Sie mich das Problem klären, da dies nicht mit dem Beispielcode der Frage repliziert werden kann. Dadurch wird die Warnung in Python 2.6 und 2.7 repliziert, wenn Sie Warnungen aktiviert haben (über das -WFlag , die PYTHONWARNINGSUmgebungsvariable oder das Warnmodul ):

>>> error = Exception('foobarbaz')
>>> error.message
__main__:1: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
'foobarbaz'

Hör auf zu benutzen .message

Ich bevorzuge repr(error), die eine Zeichenfolge zurückgibt, die den Namen des Fehlertyps, den Repr der Nachricht, falls vorhanden, und den Repr der verbleibenden Argumente enthält.

>>> repr(error)
"Exception('foobarbaz',)"

Beseitigen der Warnung während der Verwendung .message

Und so , wie Sie bekommen befreien von der DeprecationWarningist es, eine eingebaute Ausnahme Unterklasse wie die Python - Designer gedacht:

class MyException(Exception):

    def __init__(self, message, *args):
        self.message = message
        # delegate the rest of initialization to parent
        super(MyException, self).__init__(message, *args)

>>> myexception = MyException('my message')
>>> myexception.message
'my message'
>>> str(myexception)
'my message'
>>> repr(myexception)
"MyException('my message',)"

nur das .messageAttribut ohne bekommenerror.message

Wenn Sie wissen, dass es ein Argument, eine Nachricht, für die Ausnahme gab und das ist, was Sie wollen, ist es vorzuziehen, das Nachrichtenattribut zu vermeiden und nur strden Fehler zu nehmen. Sagen Sie für eine Unterklasse Exception:

class MyException(Exception):
    '''demo straight subclass'''

Und Verwendung:

>>> myexception = MyException('my message')
>>> str(myexception)
'my message'

Siehe auch diese Antwort:

Richtige Möglichkeit, benutzerdefinierte Ausnahmen in modernem Python zu deklarieren?

Aaron Hall
quelle
4

Soweit ich das beurteilen kann, vermeidet die einfache Verwendung eines anderen Namens für das Nachrichtenattribut den Konflikt mit der Basisklasse und stoppt somit die Verfallswarnung:

class MyException(Exception):

def __init__(self, message):
    self.msg = message

def __str__(self):
    return repr(self.msg)

Scheint mir ein Hack zu sein.

Vielleicht kann jemand erklären, warum die Warnung ausgegeben wird, auch wenn die Unterklasse ein Nachrichtenattribut explizit definiert. Wenn die Basisklasse dieses Attribut nicht mehr hat, sollte es kein Problem geben.

Hollister
quelle
4

In Anlehnung an die Antwort von geekQ hängt der bevorzugte Code-Ersatz davon ab, was Sie tun müssen:

### Problem
class MyException(Exception):
    """My documentation"""

try:
    raise MyException('my detailed description')
except MyException as my:
    ### Solution 1, fails in Python 2.x if MyException contains 🔥
    # with UnicodeEncodeError: 'ascii' codec can't encode characters in position 24-25: ordinal not in range(128)
    print(my)  # outputs 'my detailed description'

### Solution 2
# Works in Python 2.x if exception only has ASCII characters,
# should always work in Python 3.x
str(my)

### Solution 3
# Required in Python 2.x if you need to handle non-ASCII characters,
# such as δσφφδσ (as pointed out by jjc) or emoji 🔥 💕 🎁 💯 🌹
# but does not work in Python 3.x
unicode(my)

Manchmal haben Ausnahmen mehr als ein Argument my.args[0] nicht garantiert wird, dass alle relevanten Informationen bereitgestellt werden.

Zum Beispiel:

# Python 2.7
try:
    u'\u12345'.encode('utf-8').encode('utf-8')
except UnicodeDecodeError as e:
    print e.args[0]
    print e.args
    print str(e)

Druckt als Ausgabe:

ascii
('ascii', '\xe1\x88\xb45', 0, 1, 'ordinal not in range(128)')
'ascii' codec can't decode byte 0xe1 in position 0: ordinal not in range(128)

Es ist jedoch ein kontextsensitiver Kompromiss, weil zum Beispiel:

# Python 2.7
>>> str(SyntaxError())
'None'
# 'None' compares True which might not be expected
pzrq
quelle
1
Ja, überdenken Sie es nicht. Verwenden Sie str(my)anstelle von my.message.
Christian Long
1

Der Ratschlag zur Verwendung von str (myexception) führt zu Unicode-Problemen in Python 2.7, z.

str(Exception(u'δσφφδσ'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)

:(

unicode(Exception(u'δσφφδσ')) 

funktioniert wie erwartet und wird in Fällen bevorzugt, in denen ein Teil des Inhalts der Fehlerzeichenfolge Benutzereingaben enthält

jjc
quelle
1

pzrqs Beitrag sagt zu verwenden:

str(e)

Das war genau das, was ich brauchte.

(Wenn Sie sich in einer Unicode-Umgebung befinden, scheint Folgendes zu sein:

unicode(e)

wird funktionieren, und es scheint in einer Nicht-Unicode-Umgebung gut zu funktionieren)

Pzrq sagte viele andere gute Sachen, aber ich habe ihre Antwort wegen all der guten Sachen fast verpasst. Da ich keine 50 Punkte habe, kann ich ihre Antwort nicht kommentieren, um auf die einfache Lösung aufmerksam zu machen, die funktioniert. Da ich keine 15 habe, kann ich diese Antwort nicht abstimmen, aber ich kann posten (fühlt sich rückwärts an, aber na ja) - also hier poste ich - wahrscheinlich Punkte dafür verlieren ...

Da es mein Ziel ist, die Aufmerksamkeit auf die Antwort von pzrq zu lenken, bitte nicht glasieren und sie in allen folgenden Abschnitten verpassen. Die ersten Zeilen dieses Beitrags sind die wichtigsten.

Meine Geschichte:

Das Problem, für das ich hierher gekommen bin, war, wenn Sie eine Ausnahme von einer Klasse abfangen möchten, über die Sie keine Kontrolle haben - was dann ??? Ich werde sicherlich nicht alle möglichen Klassen unterordnen, die mein Code verwendet, um eine Nachricht aus allen möglichen Ausnahmen herauszuholen!

Ich habe verwendet:

except Exception as e:
   print '%s (%s)' % (e.message,type(e))

was, wie wir alle jetzt wissen, die Warnung gibt, nach der OP gefragt wurde (was mich hierher gebracht hat), und dies, was pzrq als eine Möglichkeit gibt, dies zu tun:

except Exception as e:
   print '%s (%s)' % (str(e),type(e))

nicht.

Ich bin nicht in einer Unicode-Umgebung, aber die Antwort von jjc hat mich gefragt, also musste ich es versuchen. In diesem Zusammenhang wird dies:

except Exception as e:
   print '%s (%s)' % (unicode(e),type(e))

was zu meiner Überraschung genau wie str (e) funktionierte - also benutze ich das jetzt.

Ich weiß nicht, ob 'str (e) / unicode (e)' der 'genehmigte Python-Weg' ist, und ich werde wahrscheinlich herausfinden, warum das nicht gut ist, wenn ich auf 3.0 komme, aber man hofft, dass die Fähigkeit, mit einem umzugehen unerwartete Ausnahme (*) ohne zu sterben und trotzdem einige Informationen daraus zu erhalten, wird niemals verschwinden ...

(*) Hmm. "unerwartete Ausnahme" - ich glaube ich stotterte nur!

rostiges Auto
quelle