PEP 348 – Auslagerung von Ausnahmen für Python 3.0
- Autor:
- Brett Cannon <brett at python.org>
- Status:
- Abgelehnt
- Typ:
- Standards Track
- Erstellt:
- 28. Jul 2005
- Post-History:
Inhaltsverzeichnis
- Zusammenfassung
- Begründung für den Wunsch nach einer Änderung
- Philosophie der Auslagerung
- Neue Hierarchie
- Unterschiede im Vergleich zu Python 2.4
- Erforderliche Oberklasse für
raise - Leere
except-Klauseln fangen Exceptions - Migrationsplan
- Abgelehnte Ideen
- DeprecationWarning erbt von PendingDeprecationWarning
- AttributeError erbt von TypeError oder NameError
- Entfernung von EnvironmentError
- Einführung von MacError und UnixError
- SystemError erbt von SystemExit
- ControlFlowException unter Exception
- Umbenennung von NameError in NamespaceError
- Umbenennung bestehender Ausnahmen
- EOFError erbt von IOError
- MemoryError und SystemError haben eine gemeinsame Oberklasse
- Gemeinsame Oberklasse für PendingDeprecationWarning und DeprecationWarning
- Entfernung von WindowsError
- Oberklasse für KeyboardInterrupt und SystemExit
- Danksagungen
- Referenzen
- Urheberrecht
Hinweis
Dieser PEP wurde abgelehnt [16].
Zusammenfassung
Python hat in Version 2.4 38 Ausnahmen (einschließlich Warnungen) im integrierten Namensraum in einer eher flachen Hierarchie. Diese Klassen sind im Laufe der Jahre ohne die Möglichkeit entstanden, aus Erfahrungen zu lernen. Dieser PEP schlägt eine Reorganisation der Hierarchie für Python 3.0 vor, wenn die Abwärtskompatibilität keine so große Rolle spielt.
Zusammen mit dieser Reorganisation wird gefordert, dass alle Objekte, die an eine raise-Anweisung übergeben werden, von einer bestimmten Oberklasse erben müssen. Dies dient dazu, Garantien über die grundlegende Schnittstelle von Ausnahmen zu haben und die natürliche Hierarchie von Ausnahmen weiter zu verbessern.
Schließlich werden leere except-Klauseln semantisch äquivalent zu except Exception geändert. Die meisten Leute verwenden derzeit leere except-Klauseln zu diesem Zweck, und mit der Reorganisation der Ausnahmenhierarchie wird dies zu einer praktikablen Standardeinstellung.
Begründung für den Wunsch nach einer Änderung
Ausnahmen sind ein kritischer Teil von Python. Während Ausnahmen traditionell verwendet werden, um Fehler in einem Programm zu signalisieren, werden sie auch für die Flusskontrolle für Dinge wie Iteratoren verwendet.
Obwohl ihre Bedeutung groß ist, fehlt ihnen die Struktur. Dies liegt daran, dass jedes Objekt als Ausnahme ausgelöst werden kann. Aus diesem Grund hat man keine Garantie, welche Art von Objekt ausgelöst wird, was jede mögliche Hierarchie von ausgelösten Objekten zerstört.
Aber Ausnahmen haben eine Hierarchie, die den Schweregrad der Ausnahme zeigt. Die Hierarchie gruppiert auch verwandte Ausnahmen zusammen, um das Abfangen in except-Klauseln zu vereinfachen. Um es den Leuten zu ermöglichen, sich auf diese Hierarchie zu verlassen, wird eine gemeinsame Oberklasse vorgeschlagen, von der alle ausgelösten Objekte erben müssen. Sie ermöglicht auch Garantien über die Schnittstelle zu ausgelösten Objekten (siehe PEP 344). Eine Diskussion darüber hat bereits auf python-dev stattgefunden [1].
Wie leere except-Klauseln derzeit sind, fangen sie *alle* Ausnahmen ab. Das kann zwar praktisch sein, ist aber für den häufigen Fall etwas übertrieben. Dank einer erforderlichen Oberklasse ist das Abfangen aller Ausnahmen so einfach wie das Abfangen einer bestimmten Ausnahme. Dies ermöglicht es, leere except-Klauseln für einen nützlicheren Zweck zu verwenden. Auch dies wurde auf python-dev diskutiert [2].
Schließlich werden leichte Änderungen an der Ausnahmenhierarchie sie in Bezug auf die Struktur viel vernünftiger machen. Durch geringfügiges Umordnen können Ausnahmen, die typischerweise nicht abgefangen werden sollten, bis zur Spitze des Ausführungsstapels propagieren und den Interpreter wie beabsichtigt beenden.
Philosophie der Auslagerung
Bei der Reorganisation der Hierarchie wurde eine allgemeine Philosophie verfolgt, die sich aus der Diskussion früherer Entwürfe dieses PEP entwickelte [4], [5], [6], [7], [8], [9]. In erster Linie ging es darum, nichts zu zerbrechen, was funktioniert. Das bedeutete, dass die Umbenennung von Ausnahmen außer Frage stand, es sei denn, der Name wurde als stark schlecht eingestuft. Das bedeutete auch, dass keine Ausnahmen entfernt wurden, es sei denn, sie wurden als wirklich fehlplatziert angesehen. Neue Ausnahmen wurden nur in Situationen eingeführt, in denen es nützlich sein könnte, eine Oberklasse einer Kategorie von Ausnahmen abzufangen. Schließlich wurde der Vererbungsbaum bestehender Ausnahmen nur geändert, wenn man der Meinung war, dass sie von vornherein wirklich fehlplatziert waren.
Für alle neuen Ausnahmen musste das richtige Suffix gewählt werden. Für diejenigen, die einen Fehler signalisieren, wird "Error" verwendet. Wenn die Ausnahme eine Warnung ist, dann "Warning". "Exception" wird verwendet, wenn keines der anderen Suffixe geeignet ist und kein spezifisches Suffix besser passt.
Danach ging es darum zu entscheiden, welche Ausnahmen von Exception erben sollten und welche nicht. Dies diente dazu, leere except-Klauseln nützlicher zu machen.
Zuletzt musste die gesamte bestehende Hierarchie von der neuen Ausnahme erben, die als erforderliche Oberklasse für alle Ausnahmen dienen soll.
Neue Hierarchie
Hinweis
Ausnahmen, die mit "strengerer Vererbung" gekennzeichnet sind, erben nicht mehr von einer bestimmten Klasse. Ein "breitere Vererbung"-Flag bedeutet, dass eine Klasse zum Vererbungsbaum der Ausnahme hinzugefügt wurde. Alle Vergleiche beziehen sich auf die Ausnahmenhierarchie von Python 2.4.
+-- BaseException (new; broader inheritance for subclasses)
+-- Exception
+-- GeneratorExit (defined in PEP 342)
+-- StandardError
+-- ArithmeticError
+-- DivideByZeroError
+-- FloatingPointError
+-- OverflowError
+-- AssertionError
+-- AttributeError
+-- EnvironmentError
+-- IOError
+-- EOFError
+-- OSError
+-- ImportError
+-- LookupError
+-- IndexError
+-- KeyError
+-- MemoryError
+-- NameError
+-- UnboundLocalError
+-- NotImplementedError (stricter inheritance)
+-- SyntaxError
+-- IndentationError
+-- TabError
+-- TypeError
+-- RuntimeError
+-- UnicodeError
+-- UnicodeDecodeError
+-- UnicodeEncodeError
+-- UnicodeTranslateError
+-- ValueError
+-- ReferenceError
+-- StopIteration
+-- SystemError
+-- Warning
+-- DeprecationWarning
+-- FutureWarning
+-- PendingDeprecationWarning
+-- RuntimeWarning
+-- SyntaxWarning
+-- UserWarning
+ -- WindowsError
+-- KeyboardInterrupt (stricter inheritance)
+-- SystemExit (stricter inheritance)
Unterschiede im Vergleich zu Python 2.4
Eine gründlichere Erklärung der Begriffe ist bei der Diskussion von Vererbungsänderungen erforderlich. Vererbungsänderungen führen entweder zu einer breiteren oder einer restriktiveren Vererbung. "Breiter" bedeutet, wenn eine Klasse einen Vererbungsbaum wie cls, A hat und dann zu cls, B, A wird. "Strenger" ist das Gegenteil.
BaseException
Die Oberklasse, von der alle Ausnahmen erben müssen. Ihr Name wurde gewählt, um zu reflektieren, dass sie an der Basis der Ausnahmenhierarchie steht und selbst eine Ausnahme ist. "Raisable" wurde als Name in Erwägung gezogen, aber verworfen, da der Name nicht richtig widerspiegelte, dass es sich selbst um eine Ausnahme handelt.
Direkte Vererbung von BaseException wird nicht erwartet und wird im Allgemeinen nicht empfohlen. Die meisten benutzerdefinierten Ausnahmen sollten stattdessen von Exception erben. Dies ermöglicht es, Exception weiterhin im häufigen Fall des Abfangens aller abzufangenden Ausnahmen zu verwenden. Direkte Vererbung von BaseException sollte nur in Fällen erfolgen, in denen eine völlig neue Kategorie von Ausnahmen gewünscht ist.
Für Fälle, in denen alle Ausnahmen blind abgefangen werden sollen, funktioniert except BaseException.
KeyboardInterrupt und SystemExit
Beide Ausnahmen liegen nicht mehr unter Exception. Dies ermöglicht es, dass leere except-Klauseln als eine praktikablere Standardeinstellung fungieren, indem Ausnahmen abgefangen werden, die von Exception erben. Da beide KeyboardInterrupt und SystemExit als Signale für den erwarteten Abbruch des Interpreters fungieren, ist das Abfangen in den häufigsten Fällen die falsche Semantik.
NotImplementedError
Erbt von Exception statt von RuntimeError.
NotImplementedError, das ursprünglich von RuntimeError geerbt hat, hat keine direkte Beziehung zu der Ausnahme, die für die Verwendung im Benutzercode als schnelle und schmutzige Ausnahme gedacht ist. Daher erbt es nun direkt von Exception.
Erforderliche Oberklasse für raise
Durch die Anforderung, dass alle Objekte, die an eine raise-Anweisung übergeben werden, von einer bestimmten Oberklasse erben müssen, sind alle Ausnahmen garantiert bestimmte Attribute zu haben. Wenn PEP 344 akzeptiert wird, sind die dort beschriebenen Attribute garantiert auf allen ausgelösten Ausnahmen vorhanden. Dies sollte die Fehlersuche erleichtern, indem die Abfrage von Informationen aus Ausnahmen viel einfacher wird.
Die vorgeschlagene Hierarchie hat BaseException als erforderliche Basisklasse.
Implementierung
Die Durchsetzung ist unkompliziert. Die Änderung von RAISE_VARARGS, um zuerst eine Vererbungskontrolle durchzuführen, bevor eine Ausnahme ausgelöst wird, sollte ausreichen. Für die C-API werden alle Funktionen, die eine Ausnahme setzen, die gleiche Vererbungskontrolle angewendet.
Leere except-Klauseln fangen Exceptions
In den meisten bestehenden Python 2.4-Codes sind leere except-Klauseln zu breit in den abgefangenen Ausnahmen. Typischerweise sollen nur Ausnahmen abgefangen werden, die einen Fehler signalisieren. Das bedeutet, dass Ausnahmen, die zum Beenden des Interpreters verwendet werden, im häufigen Fall nicht abgefangen werden sollten.
Da KeyboardInterrupt und SystemExit nun von BaseException statt von Exception erben, wird die Änderung leerer except-Klauseln, um als except Exception zu fungieren, zu einer viel vernünftigeren Standardeinstellung. Diese Änderung wird auch nur sehr wenig Code brechen, da diese Semantik das ist, was die meisten Leute für leere except-Klauseln wollen.
Die vollständige Entfernung leerer except-Klauseln wurde argumentiert. Es wurde argumentiert, dass sie sowohl Only One Way To Do It (OOWTDI) als auch Explicit Is Better Than Implicit (EIBTI) aus The Zen of Python verletzen. Aber Practicality Beats Purity (PBP), ebenfalls im Zen of Python, schlägt beide in diesem Fall. Der BDFL hat erklärt, dass leere except-Klauseln auf diese Weise funktionieren werden [14].
Implementierung
Der Compiler wird den Bytecode für except Exception ausgeben, wann immer eine leere except-Klausel erreicht wird.
Migrationsplan
Aufgrund der Komplexität und des Aufwands, der für die Hinzufügung aller in diesem PEP geplanten Funktionen erforderlich wäre, ist der Übergangsplan sehr einfach. In Python 2.5 wird BaseException hinzugefügt. In Python 3.0 treten alle verbleibenden Funktionen (erforderliche Oberklasse, Änderung der Vererbung, leere except-Klauseln werden gleichbedeutend mit except Exception) in Kraft. Um all dies in Python 2.5 abwärtskompatibel zu gestalten, wären sehr tiefe Hacks in der Ausnahmen-Maschinerie erforderlich, die fehleranfällig sein und zu einer Verlangsamung der Leistung bei geringem Nutzen führen könnten.
Um den Übergang zu erleichtern, werden die Dokumentationen geändert, um mehrere Programmierrichtlinien widerzuspiegeln
- Wenn man *alle* Ausnahmen abfangen möchte, fange BaseException ab.
- Um alle Ausnahmen abzufangen, die nicht das Beenden des Interpreters darstellen, fange Exception explizit ab.
- Fange KeyboardInterrupt und SystemExit explizit ab; verlasse dich nicht auf die Vererbung von Exception, um erfasst zu werden.
- Fange NotImplementedError immer explizit ab, anstatt sich auf die Vererbung von RuntimeError zu verlassen.
Die Dokumentation für das Modul 'exceptions' [3], das Tutorial [15] und PEP 290 müssen alle aktualisiert werden.
Abgelehnte Ideen
DeprecationWarning erbt von PendingDeprecationWarning
Dies wurde ursprünglich vorgeschlagen, da eine DeprecationWarning als eine PendingDeprecationWarning angesehen werden kann, die in der nächsten Version entfernt wird. Da aber genügend Leute der Meinung waren, dass die Vererbung logischerweise auch andersherum funktionieren könnte, wurde die Idee fallen gelassen.
AttributeError erbt von TypeError oder NameError
Attribute als Teil der Schnittstelle eines Typs zu betrachten, führte zur Idee der Vererbung von TypeError. Dies untergräbt jedoch teilweise das Konzept des Duck-Typings und wurde daher fallen gelassen.
Die Vererbung von NameError wurde vorgeschlagen, da Objekte als ihren eigenen Namensraum betrachtet werden können, in dem die Attribute leben, und wenn ein Attribut nicht gefunden wird, handelt es sich um einen Namensraumfehler. Auch dies wurde verworfen, da nicht alle diese Ansicht teilten.
Entfernung von EnvironmentError
Ursprünglich basierend auf der Idee, dass EnvironmentError eine unnötige Unterscheidung war, wurde diese Idee vom BDFL überstimmt [10].
Einführung von MacError und UnixError
Vorgeschlagen, um die Symmetrie zu WindowsError herzustellen, sagte der BDFL, dass sie nicht oft genug verwendet würden [10]. Die Idee, WindowsError dann zu entfernen, wurde vorgeschlagen und als vernünftig akzeptiert, wodurch die Idee, diese Ausnahmen hinzuzufügen, vollständig negiert wurde.
SystemError erbt von SystemExit
Vorgeschlagen, da SystemError zu einem Systemabbruch führen soll, wurde die Idee entfernt, da CriticalError dies besser anzeigt.
ControlFlowException unter Exception
Es wurde vorgeschlagen, dass ControlFlowException von Exception erben sollte. Diese Idee wurde auf der Grundlage abgelehnt, dass Kontrollfluss-Ausnahmen typischerweise nicht alle von einer einzigen except-Klausel abgefangen werden müssen.
Umbenennung von NameError in NamespaceError
NameError wird als prägnanter angesehen und lässt keine mögliche Tippfehler bei der Großschreibung von "Namespace" zu [11].
Umbenennung von RuntimeError oder Einführung von SimpleError
Die Überlegung war, dass RuntimeError in keiner Weise ein offensichtlicher Name für eine Ausnahme war, die verwendet werden sollte, wenn eine Situation keine Erstellung einer neuen Ausnahme erforderte. Die Umbenennung wurde abgelehnt, da die Ausnahme bereits im gesamten Interpreter verwendet wird [12]. Die Ablehnung von SimpleError basierte auf der Idee, dass Leute frei sein sollten, jede beliebige Ausnahme zu verwenden, und nicht eine so offensichtlich vorgeschlagene zu haben [13].
Umbenennung bestehender Ausnahmen
Verschiedene Umbenennungen wurden vorgeschlagen, aber keine wurde mehr als +0 Stimmen erhalten (Umbenennung von ReferenceError in WeakReferenceError). Die Überlegung war, dass die bestehenden Namen in Ordnung waren und sich nie jemand aktiv darüber beschwert hatte. Um Rückwärtskompatibilitätsprobleme zu minimieren und bestehende Python-Programmierer nicht zusätzlich zu belasten, wurden die Umbenennungen verworfen.
EOFError erbt von IOError
Der ursprüngliche Gedanke war, dass EOFError, da es sich direkt mit I/O befasst, von IOError erben sollte. Aber da EOFError eher als Signal für das Eintreten eines Ereignisses verwendet wird (die Erschöpfung eines I/O-Ports), sollte es nicht von einer so spezifischen Fehler-Ausnahme erben.
MemoryError und SystemError haben eine gemeinsame Oberklasse
Beide Klassen befassen sich mit dem Interpreter, also warum sollten sie keine gemeinsame Oberklasse haben? Weil eine von ihnen bedeutet, dass der Interpreter in einem Zustand ist, aus dem er sich nicht erholen sollte, während die andere es tut.
Gemeinsame Oberklasse für PendingDeprecationWarning und DeprecationWarning
Die Gruppierung von DeprecationWarning-Ausnahmen zusammen ist intuitiv sinnvoll. Aber dieser sinnvolle Gedanke lässt sich nicht gut erweitern, wenn man bedenkt, wie selten jede der beiden Warnungen verwendet wird, geschweige denn gleichzeitig.
Entfernung von WindowsError
Ursprünglich basierend auf der Idee, dass eine solche plattformspezifische Ausnahme nicht im integrierten Namensraum sein sollte. Es stellt sich jedoch heraus, dass genügend Code existiert, der die Ausnahme verwendet, um ihre Beibehaltung zu rechtfertigen.
Oberklasse für KeyboardInterrupt und SystemExit
Vorgeschlagen, um das Abfangen von Ausnahmen, die nicht von Exception erben, zu erleichtern und den Übergang zur neuen Hierarchie zu vereinfachen, wurde die Idee vom BDFL abgelehnt [14]. Das Argument, dass der bestehende Code nicht genügend Instanzen des Abfangens des Paares von Ausnahmen zeigte und daher die Belastung des integrierten Namensraums nicht rechtfertigte, wurde verwendet.
Danksagungen
Vielen Dank an Robert Brewer, Josiah Carlson, Alyssa Coghlan, Timothy Delaney, Jack Diedrich, Fred L. Drake, Jr., Philip J. Eby, Greg Ewing, James Y. Knight, MA Lemburg, Guido van Rossum, Stephen J. Turnbull, Raymond Hettinger und alle anderen, die ich übersehen habe, für die Teilnahme an der Diskussion.
Referenzen
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0348.rst
Zuletzt geändert: 2025-02-01 08:59:27 GMT