Programmiersprache Python 3.11 erweitert die Fehlerbehandlung und die Type Hints

Neben präziseren Fehlermeldungen bietet das Release eine Methode zum Gruppieren von Exceptions. Daneben soll es einen deutlichen Performance-Schub geben.

In Pocket speichern vorlesen Druckansicht 9 Kommentare lesen
Schlange, Python, Paradies

(Bild: Michael Schwarzenberger, gemeinfrei (Creative Commons CC0))

Lesezeit: 5 Min.
Inhaltsverzeichnis

Mit leichter Verspätung ist das ursprünglich für den 3. Oktober geplante Python 3.11 erschienen. Die Programmiersprache bringt Verbesserungen für den Umgang mit Fehlern und Exceptions mit und erweitert die Type Hints.

Das Release zielt zudem auf eine verbesserte Performance. Laut der Ankündigung ist CPython 3.11 bei Messungen mit der Python Benchmark Suite unter Ubuntu Linux im Schnitt 25 Prozent schneller als CPython 3.10. Version 3.11 bringt sowohl Optimierungen für die Startzeit als auch für die Runtime mit.

Für Erstere hat das Team den Cache für den Bytecode angepasst. Die für den Start essenziellen Kernmodule sind nun "eingefroren", also statisch alloziiert. Zum Verbessern der Laufzeit bringt das Release eine effizientere und schlankere Struktur der beim Aufruf von Python-Funktionen erforderlichen Stack-Frames mit. Außerdem entfällt für viele Python-Funktionsaufrufe die Verwendung des C-Stacks, was zum einen die Performance verbessern soll und zum anderen eine tiefere Rekursion erlaubt.

Das PEP 657 (Python Enhancement Proposal) "Fine-grained error locations in tracebacks" sorgt dafür, dass der Python-Interpreter bei Tracebacks nicht nur wie bisher die Zeile anzeigt, in der ein Fehler aufgetreten ist, sondern den exakten Ausdruck. Folgendes Beispiel aus der Ankündigung zeigt die neue Ausgabe:

Traceback (most recent call last):
  File "distance.py", line 11, in <module>
    print(manhattan_distance(p1, p2))
          ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "distance.py", line 6, in manhattan_distance
    return abs(point_1.x - point_2.x) + abs(point_1.y - point_2.y)
                           ^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'x'

Die Fehlerinformationen sind zudem über eine API verfügbar, mit der sich eine Verbindung der Anweisung im Bytecode zu der passenden Stelle im Sourcecode herstellen lässt. Die zugehörige Python-Methode ist codeobject.co_positions(), und die C-API bringt dafür die Funktion PyCode_Addr2Location() mit.

Die zusätzlichen Informationen erhöhen den Speicherbedarf des Python-Interpreters und die Dateigröße leicht. Wer darauf verzichten möchte, kann sie über den Kommandozeilenparameter -X no_debug_ranges beziehungsweise die Umgebungsvariable PYTHONNODEBUGRANGES unterdrücken.

Das PEP 654 "Exception Groups and except*" ermöglicht es, mehrere Exceptions zu gruppieren und gemeinsam zu werfen. Die Neuerung geht weiter als das Verketten von Exceptions, da es Ausnahmesituationen zusammenfassen kann, die nicht direkt miteinander verbunden sind. Damit lassen sich unter anderem Exceptions bei nebenläufigen Programmen elegant verarbeiten. Die neue Anweisung except* kann die ExceptionGroup beziehungsweise Teile davon behandeln.

Eine weitere Neuerung für Ausnahmesituationen kommt mit dem PEP 678 "Exceptions can be enriched with notes": Über die Methode add_note erhält eine BaseException zusätzliche Informationen, die im Traceback erscheinen.

Python ist von Grund auf dynamisch typisiert, bietet aber seit Version 3.6 Type Hints, um den Typ einer Variable festzulegen und so unter anderem Programmierfehler zu vermeiden. Python 3.11 bringt ein paar Erweiterungen für diese Form der Typisierung.

Mit dem PEP 673 "self type" können Methoden anzeigen, dass sie eine Instanz ihrer Klasse als self zurückgeben, wie in folgendem Beispiel aus dem Proposal:

class Shape:
    def set_scale(self, scale: float) -> Self:
        self.scale = scale
        return self


class Circle(Shape):
    def set_radius(self, radius: float) -> Self:
        self.radius = radius
        return self
        

Für jedes Element in einem TypeDict lässt sich dank des PEP 655: "Marking individual TypedDict items as required or not-required" festlegen, ob sie erforderlich sind oder nicht:

class Movie(TypedDict):
   title: str
   year: NotRequired[int]

m1: Movie = {"title": "Black Panther", "year": 2018}  # OK
m2: Movie = {"title": "Star Wars"}  # OK (year is not required)
m3: Movie = {"year": 2022}  # ERROR (missing required field title)

Daneben erweitert PEP 646 "Variadic generics" den Umgang von Generics. Während das mit den Type Hints eingeführte TypeVar einen Typ beispielsweise als int oder float festlegen kann,

T = TypeVar('T', int, float)

dient TypeVarTuple nicht nur als Platzhalter für einen Typ, sondern eine beliebige Zahl von Typen, die als Tupel angelegt sind. Sie können überall dort stehen, wo auch TypeVar erlaubt ist, unter anderem in Klassendefinitionen, für Parameter und für Rückgabetypen. Außerdem lassen sie sich mit regulären TypeVars kombinieren wie in folgendem Code aus der Dokumentation:

DType = TypeVar('DType')

class Array(Generic[DType, *Shape]):  # This is fine
    pass

class Array2(Generic[*Shape, DType]):  # This would also be fine
    pass

float_array_1d: Array[float, Height] = Array()     # Totally fine
int_array_2d: Array[int, Height, Width] = Array()  # Yup, fine too

Motivation für das Proposal war der Umgang mit Array-ähnlichen Strukturen unter anderem im Zusammenspiel mit NumPy, für die mit den Variadic Generics eine statische Typprüfung möglich ist.

Schließlich führt das PEP 675 "Arbitrary literal string type" die Annotation LiteralString ein, die anzeigt, dass der Typ ein beliebiges String-Literal sein darf.

Mit PEP 680 "tomllib: Support for Parsing TOML in the Standard Library" bekommt die Standardbibliothek von Python ein neues Modul zum Parsen von TOML-Inhalten (Tom’s Obvious Minimal Language). Das Format zum Beschreiben von Konfigurationen hat sich in den letzten Jahren im Python-Ökosystem etabliert. Es zielt zum einen auf gute Lesbarkeit und zum anderen darauf, sich automatisch in Datenstrukturen überführen zu lassen.

Schließlich fasst das PEP 594 "Removing dead batteries from the standard library" zahlreiche Aufräumarbeiten zusammen, die Module aus der Standardbibliothek entfernen, und PEP 670 "Convert macros to functions in the Python C API" tauscht Makros in der C-API gegen Inline- beziehungsweise reguläre Funktionen aus, um typische Fallstricke beim Verwenden von Makros in C zu umgehen.

Weitere Neuigkeiten und Änderungen in Python 3.11 lassen sich der Übersicht der Neuerungen in der Dokumentation entnehmen. Das Release ist auf der offiziellen Downloadseite verfügbar. Wer einen Blick auf die nahe Zukunft der Programmiersprache werfen möchte, findet auf der Pre-Release-Seite die erste Alpha-Version von Python 3.12.

(rme)