Kurs
Python 3.8 hat der Sprache einen neuen Operator spendiert. Offiziell heißt er Assignment Expression und verwendet den Operator :=.
Umgangssprachlich ist er als Walrus-Operator bekannt – weil er aussieht wie die Hauer eines Walrosses!
Als der Operator eingeführt wurde, gab es für Python-Verhältnisse ungewohnt viel Diskussion. Auch heute wird er eher zurückhaltend eingesetzt. Trotzdem gehört die Assignment Expression jetzt zu Python und kann in manchen Fällen sehr nützlich sein.
In diesem Tutorial schauen wir uns an, wie Pythons Walrus-Operator funktioniert und in welchen Situationen er sich anbietet.
Werde Dateningenieur
Was ist Pythons Walrus-Operator
Die Assignment Expression mit dem Operator := sieht dem geläufigeren Assignment Statement mit dem Operator = ähnlich. Es gibt jedoch einen wichtigen Unterschied.
Ein Statement ist eine Codeeinheit, die eine Aktion ausführt. Ein Expression ist ein Statement, das zu einem Wert ausgewertet werden kann. Ein Ausdruck liefert also einen Wert, während Statements, die keine Ausdrücke sind, keinen Wert zurückgeben.
Die Python-REPL (Read, Evaluate, Print, Loop) zeigt den Wert eines Ausdrucks unmittelbar nach der Auswertung an. Bei der Ausführung eines Statements, das kein Ausdruck ist, erscheint hingegen kein Wert:
>>> import random # Not an expression
>>> 10 * 2 # An expression
20
Das import-Statement ergibt keinen Wert. 10 * 2 ist hingegen ein Ausdruck, der zu einem Wert ausgewertet wird.
Das Assignment Statement mit = weist ein Objekt einer Variablen oder Referenz zu, gibt den Wert aber nicht zurück. Die Assignment Expression, also der Walrus-Operator, führt dieselbe Zuweisung aus, gibt das Objekt jedoch zusätzlich zurück:
>>> value = 10 # Assignment statement
>>> value
10
>>> (value := 20) # Assignment expression
20
Beachte: Das Assignment Statement liefert keinen Wert zurück – die Assignment Expression schon. Sie kombiniert daher mehrere Schritte:
- Sie wertet den Ausdruck rechts vom Operator
:=aus. - Sie weist das Ergebnis einem Variablennamen zu.
- Sie gibt den Wert zurück.
Klammern sind in solchen Fällen Pflicht, wenn du die Assignment Expression statt eines Assignment Statements verwendest, um Mehrdeutigkeit zu vermeiden. Weiter unten gehen wir genauer auf die Syntaxregeln des Walrus-Operators ein.
Eine Assignment Expression kann überall dort stehen, wo in Python Ausdrücke erlaubt sind. Ein Beispiel:
print(f"The number is {(value := 50)}")
print(value)
The number is 50
50
Der Variablenname value wird innerhalb der f-String im ersten Aufruf definiert. Gleichzeitig wird der Integer direkt in die geschweiften Klammern der f-String übergeben. In f-Strings dürfen nur Ausdrücke in den Klammern stehen – daher ist hier ein Assignment Statement nicht möglich.
Einsatzszenarien für Pythons Walrus-Operator
Zwei typische Einsatzfälle sind das Vereinfachen und das Optimieren von Code.
Code vereinfachen
Häufig kannst du mit dem Walrus-Operator Code vereinfachen, wenn eine Variable vor ihrer Nutzung definiert werden müsste – etwa in Bedingungen. Sieh dir diese while-Schleife an:
import random
value = random.randint(1, 20)
while value < 18:
print(value)
value = random.randint(1, 20)
7
5
1
11
12
1
9
Der Code führt den while-Block aus, solange der Zufallswert kleiner als 18 ist. Die Variable value muss vor der Schleife definiert sein, damit sie im while-Statement benutzt werden kann. Am Ende des Blocks wird value neu gesetzt.
Mit der Assignment Expression := reduzierst du die Codezeilen und rufst random.randint() nur an einer Stelle auf:
import random
while (value := random.randint(1, 20)) < 18:
print(value)
1
9
2
4
9
15
11
Bei jedem neuen Schleifendurchlauf wird eine neue Zufallszahl erzeugt und value zugewiesen.
Code optimieren
Betrachten wir Listen-Komprehension. Der folgende Code filtert eine Liste von Datums-Strings und erzeugt eine Liste von datetime.datetime-Objekten, die nur Daten eines bestimmten Jahres enthalten:
from datetime import datetime
def format_date(date_str):
return datetime.strptime(date_str, "%Y-%m-%d")
dates = ["2024-01-01", "2022-12-31", "2024-06-15", "2023-08-23", "2024-11-30"]
formatted_dates = [
format_date(date)
for date in dates
if format_date(date).year == 2024
]
print(formatted_dates)
[datetime.datetime(2024, 1, 1, 0, 0), datetime.datetime(2024, 6, 15, 0, 0), datetime.datetime(2024, 11, 30, 0, 0)]
Die Funktion format_date() nimmt ein Datum im Format yyyy-mm-dd entgegen und liefert ein datetime.datetime-Objekt. Eines seiner Attribute ist .year für die Jahreszahl.
In der Ergebnisliste landen nur die drei Daten aus 2024, nun als datetime.datetime-Objekte statt als Strings. Die Listen-Komprehension enthält eine if-Klausel, die format_date() aufruft. Gleichzeitig wird format_date() im ersten Ausdruck der Komprehension erneut aufgerufen, um die Werte zu erzeugen. Die Funktion mit demselben Argument doppelt aufzurufen ist ineffizient – besonders, wenn sie ein Performance-Engpass ist.
Eine Möglichkeit, den doppelten Aufruf zu vermeiden, wäre, das Ergebnis vorab in eine Variable zu schreiben. Mit einem Assignment Statement ist das aber nicht mit einer Listen-Komprehension kombinierbar – du bräuchtest dann eine normale for-Schleife:
from datetime import datetime
def format_date(date_str):
return datetime.strptime(date_str, "%Y-%m-%d")
dates = ["2024-01-01", "2022-12-31", "2024-06-15", "2023-08-23", "2024-11-30"]
formatted_dates = []
for date in dates:
formatted_date = format_date(date)
if formatted_date.year == 2024:
formatted_dates.append(formatted_date)
print(formatted_dates)
[datetime.datetime(2024, 1, 1, 0, 0), datetime.datetime(2024, 6, 15, 0, 0), datetime.datetime(2024, 11, 30, 0, 0)]
Der Walrus-Operator bietet hier eine Alternative, die mit einer Listen-Komprehension kompatibel ist: Das von der Funktion zurückgegebene Datum kann in einem Ausdruck zugleich einer Variablen zugewiesen und zurückgegeben werden:
from datetime import datetime
def format_date(date_str):
return datetime.strptime(date_str, "%Y-%m-%d")
dates = ["2024-01-01", "2022-12-31", "2024-06-15", "2023-08-23", "2024-11-30"]
formatted_dates = [
formatted_date
for date in dates
if (formatted_date := format_date(date)).year == 2024
]
print(formatted_dates)
[datetime.datetime(2024, 1, 1, 0, 0), datetime.datetime(2024, 6, 15, 0, 0), datetime.datetime(2024, 11, 30, 0, 0)]
Die Assignment Expression in der Komprehension ist der geklammerte Ausdruck (formatted_date := format_date(date)). Er ruft format_date() auf, weist das Ergebnis der Variablen formatted_date zu und gibt es zurück. Da der Ausdruck zu einem datetime.datetime-Objekt auswertet, wird dessen Attribut .year abgefragt und mit 2024 verglichen.
Die if-Klausel in der Listen-Komprehension wird vor dem Ausdruck ausgewertet, der die Listenwerte erzeugt. Daher steht der Walrus-Operator in der if-Klausel, und die dort definierte Variable dient als erster Ausdruck der Komprehension.
Die Kontroverse um Pythons Walrus-Operator
Nicht alle Python-Entwickler sind Fans der Assignment Expression. Manche sehen darin eine hilfreiche Vereinfachung, andere finden, dass der Code dadurch weniger lesbar wird und bevorzugen längere Varianten. Lesbarkeit ist subjektiv – dementsprechend gehen die Meinungen auseinander.
Das letzte Beispiel zeigt die Abwägung: Ohne Walrus-Operator brauchst du zunächst eine leere Liste und fügst dann in einer for-Schleife Werte an. Diese Lösung ist länger und weniger effizient als eine Listen-Komprehension, die für solche Aufgaben optimiert ist.
Für einige Entwickler ist die Standard-for-Schleife dennoch leserlicher – insbesondere, wenn Performance keine Rolle spielt.
Viele Styleguides empfehlen weiterhin, die Assignment Expression zu vermeiden oder nur sehr gezielt einzusetzen.
Pythons Walrus-Operator: Syntaxregeln
Gerade am Anfang stolpert man leicht über Syntaxfehler – die Regeln sind speziell.
Um Verwechslungen zwischen Assignment Statement = und Assignment Expression := zu vermeiden, gibt es keine Situation, in der beide syntaktisch gültig wären. Deshalb müssen Assignment Expressions häufig in Klammern gesetzt werden:
(value := 20) # Valid
value := 20 # Invalid (SyntaxError)
Diese Klammerpflicht ist das Gegenteil der Regel für Assignment Statements – sie dürfen nicht geklammert werden.
Allerdings brauchen Assignment Expressions nicht immer Klammern:
import random
if value := random.randint(0, 3):
print(f"{value} is greater than 0")
1 is greater than 0
Die Assignment Expression im if-Statement erzeugt eine Zufallszahl zwischen 0 und 3 und weist sie value zu. Die Integer 1, 2 und 3 sind truthy, der Code im if-Block läuft also für diese Werte. Liefert random.randint() jedoch 0 (falsy), wird der Block übersprungen.
Nach einem if-Schlüsselwort ist ein Assignment Statement nicht erlaubt. Es gibt also keine Verwechslungsgefahr, und Klammern sind hier nicht nötig.
Dieses Beispiel illustriert auch einen weiteren wichtigen Punkt: Der Geltungsbereich der zugewiesenen Variable folgt den üblichen LEGB-Regeln. Daher ist value im Beispiel im gesamten globalen Scope verfügbar:
import random
if value := random.randint(0, 3):
print(f"{value} is greater than 0")
print(value)
2 is greater than 0
2
Innerhalb einer Funktionsdefinition ist die Variable hingegen nur lokal verfügbar – genau wie bei Variablen aus Assignment Statements.
Ein weiteres potenzielles Stolperfeld: Wir erweitern das Beispiel um einen Vergleichsoperator im if-Statement:
import random
if value := random.randint(0, 3) < 2:
print(f"{value} is less than 2")
True is less than 2
Die Ausgabe verrät das Problem: value ist hier ein boolescher Wert True statt eines Integers. Der Walrus-Operator hat die niedrigste Priorität unter allen Operatoren – nur das Komma steht darunter. Daher wird der Kleiner-als-Operator < zuerst ausgewertet, und dessen Ergebnis wird value zugewiesen. Klammern schaffen Abhilfe:
import random
if (value := random.randint(0, 3)) < 2:
print(f"{value} is less than 2")
0 is less than 2
Durch die Klammern hat die Assignment Expression nun die höchste Priorität. Die von random.randint() gelieferte Zahl wird value zugewiesen und anschließend mit 2 verglichen.
Im Gegensatz zum Assignment Statement darf der Walrus-Operator nur an Variablennamen zuweisen – nicht an Subskripte oder Attribute:
(name := "James") # Valid
points = {"James": 10, "Mary": 20}
(points["Sarah"] := 30) # Invalid
class Article:
pass
article = Article()
(article.title := "The Walrus Operator") # Invalid
Eine Zuweisung per Subskript oder an ein Attribut ist mit dem Walrus-Operator nicht möglich.
Fazit
Die Assignment Expression mit der Syntax :=, oft Walrus-Operator genannt, ist eine relativ neue Ergänzung für Python. Gewöhnlicher ist das Assignment Statement mit =, um einen Wert einem Variablennamen zuzuweisen.
Der Walrus-Operator erweitert diese Zuweisung jedoch, indem er den zugewiesenen Wert auch zurückgibt – damit ist die Zuweisung selbst ein Ausdruck. Entsprechend kannst du die Assignment Expression überall dort verwenden, wo in Python Ausdrücke erlaubt sind.
In der Python-Community gehen die Meinungen über Nutzen und Lesbarkeit auseinander. Daher wird der Walrus-Operator seltener eingesetzt als andere Neuerungen. Es ist gut, ihn zu beherrschen – aber setze ihn mit Bedacht ein!
Werde Dateningenieur
Ich habe Physik und Mathematik auf UG-Ebene an der Universität Malta studiert. Dann zog ich nach London und machte meinen Doktor in Physik am Imperial College. Ich habe an neuartigen optischen Techniken zur Abbildung der menschlichen Netzhaut gearbeitet. Jetzt konzentriere ich mich darauf, über Python zu schreiben, über Python zu kommunizieren und Python zu unterrichten.
