Python Lambda: So nutzt du anonyme Funktionen
Lambda-Funktionen gibt es bereits seit Python 1.0 als Werkzeug für die funktionale Programmierung. Heutzutage wurden sie in vielen Bereichen durch modernere Techniken ersetzt. Dennoch existieren spezifische Einsatzgebiete, die erfahrene Python-Entwickler:innen kennen sollten.
Was sind Lambda-Funktionen in Python?
Hinter dem Begriff „Lambda-Funktion“ verbirgt sich in Python eine anonyme Funktion. Um eine solche zu erstellen, nutzt du das lambda-Schlüsselwort. Ein Lambda-Ausdruck setzt sich aus diesem lambda-Key-Word, einer Liste von Argumenten, einem Doppelpunkt sowie einem einzelnen Ausdruck („Expression“) zusammen. Dieser Ausdruck wird beim Aufruf mit den entsprechenden Argumenten verarbeitet:
lambda argument: expressionFunktionen bilden das Fundament fast jeder Programmiersprache und sind die kleinsten Bausteine für wiederverwendbaren Code. Normalerweise werden Funktionen in Python mit dem def-Schlüsselwort definiert. Als Beispiel dient hier die Quadratfunktion, die eine Zahl mit sich selbst multipliziert:
# Define square function
def square(num):
return num * num
# Show that it works
assert square(9) == 81pythonNeben der klassischen Definition via def bietet Python eben auch die „Lambdas“. Das sind kompakte, anonyme (also namenlose) Funktionen für Ausdrücke mit Parametern. Du kannst Lambdas überall dort einsetzen, wo eine Funktion verlangt wird, oder sie an einen Variablennamen binden. Hier siehst du die Quadratfunktion als Lambda-Ausdruck:
# Create square function
squared = lambda num: num * num
# Show that it works
assert squared(9) == 81pythonWas unterscheidet lambda von def?
Es mag ungewöhnlich wirken, dass Python mit lambda und def zwei Wege zur Funktionserstellung bietet. Tatsächlich ist lambda kein eigenständiges Feature, sondern eine alternative Schreibweise, um kurze Funktionen direkt vor Ort zu erstellen. Jede lambda-Funktion lässt sich auch mit def umsetzen – umgekehrt gilt das jedoch nicht immer.
Rein syntaktisch sind beides Schlüsselwörter. Ein wesentlicher Unterschied liegt in Pythons klarer Trennung zwischen Anweisung („Statement“) und Ausdruck („Expression“). Vereinfacht gesagt steuern Anweisungen den Programmablauf, während Ausdrücke einen konkreten Wert liefern.
Mit def startest du eine Anweisung (ein „Compound Statement“), die weitere Befehle enthalten kann. Nur innerhalb dieser def-Struktur dürfen return-Anweisungen stehen, um beim Funktionsaufruf einen Wert zurückzugeben.
Im Gegensatz dazu leitet lambda einen Ausdruck ein, der keinerlei Anweisungen enthalten darf. Der Lambda-Ausdruck nimmt Argumente an und gibt eine anonyme Funktion zurück. Sobald diese aufgerufen wird, berechnet das Programm den enthaltenen Ausdruck mit den Werten und liefert das Ergebnis.
Welche Grenzen haben Python-Lambda-Ausdrücke?
world4you empfiehlt, Funktionen primär zu benennen, da dies die Lesbarkeit erhöht – Python schränkt Lambdas daher bewusst ein. Das zwingt dich dazu, die Logik deines Codes klarer zu strukturieren.
Im Vergleich zu Funktionen via def können Lambdas keine Anweisungen ausführen. Daher lassen sich Elemente wie if oder for nicht innerhalb einer Lambda-Funktion nutzen. Auch das gezielte Auslösen von Fehlern via raise ist hier nicht möglich.
Ein Lambda darf nur einen einzigen Ausdruck enthalten. Zudem sind keine Typ-Annotationen erlaubt. In der modernen Python-Entwicklung greift man für viele frühere Lambda-Szenarien lieber zu anderen Lösungen, allen voran den Comprehensions.
Wann kommen Lambda-Funktionen in Python zum Einsatz?
Grundsätzlich stammen Lambdas aus der Welt der funktionalen Programmierung. Während anonyme Funktionen in Sprachen wie JavaScript allgegenwärtig sind, nutzt du sie in Python vor allem für kleine, lokale Aufgaben. Wir haben die gängigsten Szenarien für dich zusammengefasst.
Lambdas in Funktionen höherer Ordnung nutzen
Häufig triffst du Lambdas im Mix mit Funktionen höherer Ordnung wie map(), filter() oder reduce() an. Damit bearbeitest du die Inhalte eines „Iterable“, ohne klassische Schleifen zu schreiben. Solche „higher-order functions“ zeichnen sich dadurch aus, dass sie Funktionen als Parameter annehmen oder selbst eine Funktion ausgeben.
Die map()-Funktion kombiniert eine Funktion mit einem Iterable und wendet erstere auf jedes Element an. Wenn wir etwa Quadratzahlen berechnen wollen, nutzen wir map() und übergeben einen Lambda-Ausdruck für die Quadrierung. So wird die Operation auf die gesamte Liste angewendet:
nums = [3, 5, 7]
# Square numbers using using `map()` and `lambda`
squares = map(lambda num: num ** 2, nums)
# Show that it works
assert list(squares) == [9, 25, 49]pythonSeit Python 3.0 liefern map() und filter() ein Iterable statt einer direkten Liste. Mit dem list()-Befehl wandeln wir dieses Ergebnis in eine druckbare Liste um.
Heute gilt die List-Comprehension als modernerer Weg, um Iterables zu verarbeiten. Statt map() und Lambda zu mischen, beschreiben wir den Vorgang direkt:
nums = [3, 5, 7]
# Square numbers using list comprehension
squares = [num ** 2 for num in nums]
# Show that it works
assert squares == [9, 25, 49]pythonMit filter() kannst du Inhalte eines Iterable filtern. Hier im Beispiel lassen wir nur gerade Quadratzahlen zu:
# List of numbers 1–4
nums = [1, 2, 3, 4]
# Square each number
squares = list(map(lambda num: num ** 2, nums))
# Filter out the even squares
even_squares = filter(lambda square: square % 2 == 0, squares)
# Show that it works
assert list(even_squares) == [4, 16]pythonAuch hier ist die List-Comprehension meist die bessere Wahl, um ohne Lambdas zum selben Ziel zu kommen. Wir nutzen den if-Teil, um die ungeraden Zahlen direkt auszusortieren:
# List of numbers 1–4 squared
squares = [num ** 2 for num in range(1, 5)]
# Filter out the even squares
even_squares = [square for square in squares if square % 2 == 0]
# Show that it works
assert even_squares == [4, 16]pythonPythons reduce()-Funktion gehört seit Version 3.0 nicht mehr zum Standard-Umfang, sondern befindet sich im functools-Modul.
Lambdas als Schlüssel-Funktionen einsetzen
Obwohl Comprehensions viele Aufgaben übernommen haben, gibt es einen Bereich, in dem Lambdas glänzen: die sogenannten „Key-Functions“ oder Schlüssel-Funktionen.
Vergleichsfunktionen wie sorted(), min() und max() arbeiten mit Iterables und vergleichen deren Elemente. Über den optionalen key-Parameter kannst du eine Logik definieren, die für jedes Element den maßgeblichen Vergleichswert bestimmt.
Stell dir vor, du hast eine Liste mit Dateinamen von Bildern, die du sortieren willst. Alle beginnen mit img, gefolgt von einer Nummer:
# List of image file names
images = ['img1', 'img2', 'img30', 'img3', 'img22', 'img100']pythonNutzt du die Standard-Funktion sorted(), greift die lexikographische Sortierung. Das führt dazu, dass Ziffern einzeln bewertet werden und etwa ['1', '100', '2'] vor ['1', '2', '100'] landet. Das Ergebnis ist meist nicht das, was du eigentlich willst:
# Sort using lexicographic order
sorted_image = sorted(images)
# Show that it works
assert sorted_image == ['img1', 'img100', 'img2', 'img22', 'img3', 'img30']pythonUm die Liste korrekt zu sortieren, nutzen wir einen lambda-Ausdruck als Schlüssel-Funktion. Dieser extrahiert den Zahlenwert aus dem Namen, damit sorted() richtig sortieren kann:
# Extract numeric component and sort as integers
sorted_image = sorted(images, key=lambda name: int(name[3:]))
# Show that it works
assert sorted_image == ['img1', 'img2', 'img3', 'img22', 'img30', 'img100']pythonDa diese Logik oft nur an einer Stelle gebraucht wird, ist eine extra definierte Funktion meist überflüssig. Lambdas sind hier das perfekte Werkzeug. Schauen wir uns weitere Beispiele an.
Auch min() und max() akzeptieren solche Schlüssel-Funktionen, um das kleinste oder größte Element zu finden. Was genau als „klein“ oder „groß“ gilt, entscheidest du über diese Funktion.
Bei einfachen Zahlenlisten ist die Definition von „Minimum“ und „Maximum“ eindeutig. Hier ist kein Extra-Aufwand nötig:
nums = [42, 69, 51, 13]
assert min(nums) == 13
assert max(nums) == 69pythonOhne explizite Angabe nutzt Python die Identitäts-Funktion f(x) = x, was einem einfachen Lambda wie lambda x: x entspricht.
Komplizierter wird es bei komplexeren Daten, etwa einer Liste von Dicts mit Namen und Alter. Wonach sollen min() und max() hier bewerten? Hier kommt wieder die Schlüssel-Funktion ins Spiel.
Zuerst brauchen wir Beispieldaten. Wir definieren eine Funktion Person() als Konstruktor:
# Constructor function for dict representing a person
def Person(name, age):
return {'name': name, 'age': age}
# Check that it works as expected
assert Person('Jim', 42) == {'name': 'Jim', 'age': 42}pythonDamit erstellen wir eine Personenliste:
# Create list of people
people = [Person('Jim', 42), Person('Jack', 51), Person('John', 69)]pythonUm nun die älteste Person zu finden, nutzen wir max() und generieren per Lambda eine Funktion, die gezielt das Alter aus den Dicts für den Vergleich herauszieht:
# Find the oldest person
oldest = max(people, key=lambda person: person['age'])
# Check that it works
assert oldest == Person('John', 69)pythonDas klappt analog für min(). Falls du die Logik mehrfach an einer Stelle benötigst, kannst du das Lambda auch vorab einer Variablen zuweisen, um den Code übersichtlicher zu halten:
# Define key function to compare people by age
by_age = lambda person: person['age']
# Find the youngest person
youngest = min(people, key=by_age)
# Check that it works
assert youngest == Person('Jim', 42)pythonClosures mit Python-Lambdas erstellen
Ein weiteres Einsatzfeld für Lambdas sind sogenannte „Closures“. Das sind Funktionen, die von anderen Funktionen erstellt werden und dabei bestimmte Werte „einschließen“ (speichern). So erstellst du effizient ganze Funktionsfamilien, wie etwa Potenzfunktionen. Mit einem Lambda sparst du dir die Definition einer inneren Funktion:
# Define constructor function for power functions
def power(n):
return lambda num: num ** n
# Create square and cubic functions as closures
square = power(2)
cubic = power(3)
# Show that it works
assert square(10) == 100
assert cubic(10) == 1000pythonImmediately Invoked Function Expression (IIFE)
Das IIFE-Muster (ausgesprochen „iffy“) ist eigentlich aus JavaScript bekannt. Dabei wird eine anonyme Funktion definiert und sofort im Anschluss ausgeführt.
In Python sind IIFEs aufgrund der Lambda-Beschränkungen selten sinnvoll, aber technisch möglich. Du setzt den Lambda-Ausdruck einfach in Klammern:
(lambda num: num * num)pythonUnd fügst ein zweites Klammerpaar für die Argumente hinzu:
assert (lambda num: num * num)(3) == 9python