Was ist neu in Python 3.9?#

Mit Python 3.9 wird erstmals ein neuer Release-Zyklus verwendet: zukünftig sollen jährlich neue Releases erscheinen (s.a. PEP 602). Die Entwickler*innen erhoffen sich hiervon u.a. schnellere Rückmeldungen zu neuen Features.

Mit dem ersten veröffentlichten Release-Kandidaten soll Python auch eine stabile Binärschnittstelle (engl. application binary interface, ABI) erhalten: es soll nun keine ABI-Änderungen mehr in der 3.9-Reihe geben womit Erweiterungsmodule nicht mehr für jede Version neu kompiliert werden müssen.

Weitere Informationen erhaltet ihr in What’s New In Python 3.9.

Im Folgenden gebe ich Euch einen kurzen Überblick über einige der neuen Features.

Installation#

Überprüfen#

[1]:
!python3 -V
Python 3.9.0rc1

or

[2]:
import sys


assert sys.version_info[:2] >= (3, 9)

PEP 584: Dictionary Merge- und Update-Operatoren#

Für die built-in dict-Klasse gibt es nun ähnliche Operatoren wie zum Verketten von Listen: Merge (|) und Update (|=). Damit werden verschiedene Nachteile der bisherigen Methoden dict.update, {**d1, **d2} und collections.ChainMap beseitigt.

Beispiel ipykernel/ipykernel/kernelapp.py#

[3]:
from IPython.core.application import (  # type:ignore[attr-defined]
    BaseIPythonApplication,
    base_aliases,
    base_flags,
    catch_config_error,
)


kernel_aliases = dict(base_aliases)
kernel_aliases.update(
    {
        "ip": "IPKernelApp.ip",
        "hb": "IPKernelApp.hb_port",
        "shell": "IPKernelApp.shell_port",
        "iopub": "IPKernelApp.iopub_port",
        "stdin": "IPKernelApp.stdin_port",
        "control": "IPKernelApp.control_port",
        "f": "IPKernelApp.connection_file",
        "transport": "IPKernelApp.transport",
    }
)

kernel_flags = dict(base_flags)
kernel_flags.update(
    {
        "no-stdout": (
            {"IPKernelApp": {"no_stdout": True}},
            "redirect stdout to the null device",
        ),
        "no-stderr": (
            {"IPKernelApp": {"no_stderr": True}},
            "redirect stderr to the null device",
        ),
        "pylab": (
            {"IPKernelApp": {"pylab": "auto"}},
            """Pre-load matplotlib and numpy for interactive use with
        the default matplotlib backend.""",
        ),
        "trio-loop": (
            {"InteractiveShell": {"trio_loop": False}},
            "Enable Trio as main event loop.",
        ),
    }
)

kann vereinfacht werden mit:

[4]:
kernel_aliases = base_aliases | {
    "ip": "KernelApp.ip",
    "hb": "KernelApp.hb_port",
    "shell": "KernelApp.shell_port",
    "iopub": "KernelApp.iopub_port",
    "stdin": "KernelApp.stdin_port",
    "parent": "KernelApp.parent",
}
if sys.platform.startswith("win"):
    kernel_aliases["interrupt"] = "KernelApp.interrupt"

kernel_flags = base_flags | {
    "no-stdout": (
        {"KernelApp": {"no_stdout": True}},
        "stdout auf das Nullgerät umleiten",
    ),
    "no-stderr": (
        {"KernelApp": {"no_stderr": True}},
        "stderr auf das Nullgerät umleiten",
    ),
}

Beispiel matplotlib/legend.py#

[ ]:
hm = default_handler_map.copy()
hm.update(self._custom_handler_map)
return hm

kann vereinfacht werden mit:

[ ]:
return default_handler_map | self._handler_map

PEP 616: removeprefix() und removesuffix() für String-Methoden#

Mit str.removeprefix(prefix) und str.removesuffix(suffix) lassen sich nun einfach Präfixe und Suffixe entfernen. Auch für bytes, bytearray-Objekte und collections.UserString-Zeichenketten wurden ähnliche Methoden hinzugefügt. Insgesamt soll dies zu weniger zerbrechlichem, performanterem und besser lesbarem Code führen.

Beispiel find_recursionlimit.py#

[ ]:
if test_func_name.startswith("test_"):
    print(test_func_name[5:])
else:
    print(test_func_name)

kann vereinfacht werden mit:

[ ]:
print (test_func_name.removeprefix ("test_"))

Beispiel deccheck.py#

[ ]:
if funcname.startswith("context."):
    self.funcname = funcname.replace("context.", "")
    self.contextfunc = True
else:
    self.funcname = funcname

kann vereinfacht werden mit:

[ ]:
self.contextfunc = funcname.startswith ("context.")
self.funcname = funcname.removeprefix ("context.")

PEP 585: Zusätzliche generische Typen#

In Type Annotations können nun z.B. list oder dict als generische Typen direkt verwendet werden – sie müssen nicht mehr extra aus typing importiert werden. Das Importieren von typing ist damit deprecated.

Beispiel#

[ ]:
def greet_all(names: list[str]) -> None:
    for name in names:
        print("Hello", name)

PEP 617: Neuer PEG-Parser#

Python 3.9 verwendet nun einen PEG (Parsing Expression Grammar)-Parser anstelle des bisherigen LL-Parser. Dies hat u.a. folgende Vorteile:

  • das Parsen abstrakter Syntaxbäume (engl. Abstract Syntax Trees, AST) vereinfacht sich erheblich

  • Left recursion wird möglich

  • Das Erstellen konkreter Syntaxbäume (engl. Concrete Syntax Trees, CST) wird möglich

Der neue Parser ist damit flexibler und soll vor allem beim Entwerfen neuer Sprachfunktionen genutzt werden. Das ast-Modul verwendet den neuen Parser schon jetzt, ohne dass sich an der Ausgabe etwas geändert hätte.

In Python 3.10 wird der alte Parser und alle davon abhängigen Funktionen – hauptsächlich das veraltete parser-Modul – gelöscht. Nur in Python 3.9 könnt ihr auf der Kommandozeile mit -X oldparser oder mit der Umgebungsvariable PYTHONOLDPARSER=1 zum LL-Parser zurückkehren.

PEP 615: Unterstützung für die IANA-Zeitzonendatenbank in der Standardbibliothek#

Das neue zoneinfo-Standardmodul bringt Unterstützung für die IANA-Zeitzonendatenbank in die Standardbibliothek.

[7]:
from datetime import datetime, timedelta
from zoneinfo import ZoneInfo

Pacific Daylight Time:

[8]:
dt = datetime(2020, 10, 31, 12, tzinfo=ZoneInfo("America/Los_Angeles"))
print(dt)
2020-10-31 12:00:00-07:00
[9]:
dt.tzname()
[9]:
'PDT'

Pacific Standard Time:

[10]:
dt += timedelta(days=7)
print(dt)
2020-11-07 12:00:00-08:00
[11]:
print(dt.tzname())
PST