Lambda-functies worden al sinds Python 1.0 gebruikt voor func­ti­o­neel pro­gram­me­ren. De laatste jaren zijn er echter andere tech­nie­ken po­pu­lair­der geworden, die lambda’s gro­ten­deels hebben vervangen. Toch zijn er nog steeds een aantal ge­spe­ci­a­li­seer­de toe­pas­sin­gen voor lambda’s die ervaren Python-pro­gram­meurs moeten kennen.

Wat zijn lambda-functies in Python?

In Python verwijst een lambda-functie naar een anonieme functie. Python gebruikt het sleu­tel­woord lambda om een lambda-functie te maken. Een lambda-expressie bestaat uit het sleu­tel­woord lambda, gevolgd door een lijst met ar­gu­men­ten, een dubbele punt en een enkele expressie. De expressie wordt voorzien van de ar­gu­men­ten en ge­ë­va­lu­eerd wanneer de lambda-functie wordt aan­ge­roe­pen:

lambda argument: expression

Functies zijn een fun­da­men­teel taal­con­struct in bijna elke pro­gram­meer­taal en vormen de kleinste her­bruik­ba­re eenheid van code. Functies in Python worden doorgaans ge­de­fi­ni­eerd met het sleu­tel­woord def. Als voorbeeld laten we u de kwa­dra­ti­sche functie zien, die een getal met zichzelf ver­me­nig­vul­digt:

# Define square function
def square(num):
    return num * num
# Show that it works
assert square(9) == 81
python

Het sleu­tel­woord def is een bekende manier om functies in Python te de­fi­ni­ë­ren, maar de taal heeft ook lambda’s. Dit zijn anonieme functies die een uit­druk­king met pa­ra­me­ters de­fi­ni­ë­ren. Lambda’s kunnen overal worden gebruikt waar een functie wordt verwacht of aan een naam kan worden toe­ge­we­zen. Hier ziet u de lambda-uit­druk­king die gelijk is aan de kwa­dra­ti­sche functie:

# Create square function
squared = lambda num: num * num
# Show that it works
assert squared(9) == 81
python

Wat is het verschil tussen lambda en def?

Het lijkt misschien vreemd dat je in Python functies kunt maken met zowel lambda als def. Lambda is echter geen aparte functie, maar gewoon een andere manier om lokaal korte functies te maken. Elke functie die met lambda wordt gemaakt, kan ook met def worden gemaakt. Omgekeerd is dit echter niet het geval.

Op syn­tac­tisch niveau zijn lambda en def beide sleu­tel­woor­den. Een be­lang­rijk verschil tussen beide is de strikte scheiding tussen sta­te­ments en ex­pres­sies in Python. Sta­te­ments zijn stappen in de uit­voe­ring van code, terwijl ex­pres­sies worden ge­ë­va­lu­eerd tot een waarde.

Def begint een statement, of meer specifiek een sa­men­ge­steld statement, dat verdere sta­te­ments bevat. Er mogen alleen return sta­te­ments voorkomen binnen een def statement. Een return statement re­tour­neert een waarde wanneer de functie ge­de­fi­ni­eerd met def wordt aan­ge­roe­pen.

In te­gen­stel­ling tot de def -in­struc­tie begintlambda een expressie die geen in­struc­ties mag bevatten. De lambda-expressie neemt een of meer ar­gu­men­ten en re­tour­neert een anonieme functie. Wanneer de lambda-functie wordt aan­ge­roe­pen, wordt de expressie die erin staat ge­ë­va­lu­eerd met de door­ge­ge­ven ar­gu­men­ten en wordt deze ge­re­tour­neerd.

Wat zijn de be­per­kin­gen van Python’s lambda-ex­pres­sies?

Python heeft het nut van lambda-functies bewust beperkt, omdat het meestal beter is om functies een naam te geven. Dit dwingt pro­gram­meurs om na te denken over de betekenis van de functie en om on­der­de­len duidelijk te on­der­schei­den.

Lambdas kunnen geen in­struc­ties bevatten, in te­gen­stel­ling tot de body van een functie die is ge­de­fi­ni­eerd met het sleu­tel­woord def. Het is daarom niet mogelijk om if, for, enz. te gebruiken in een lambda-functie. Het is ook niet mogelijk om een uit­zon­de­ring te activeren, omdat hiervoor een raise nodig is.

Lambda-functies in Python kunnen één enkele expressie bevatten die wordt ge­ë­va­lu­eerd wanneer deze wordt aan­ge­roe­pen. Type-an­no­ta­ties kunnen niet worden gebruikt binnen de lambda-expressie. Te­gen­woor­dig maken de meeste ge­bruiks­sce­na­rio’s van lambda-functies in Python gebruik van andere tech­nie­ken, zoals com­pre­hen­si­ons.

Ver­schil­len­de toe­pas­sin­gen voor lambda-functies in Python

Lambda’s zijn afgeleid van func­ti­o­neel pro­gram­me­ren. In sommige talen, zoals Ja­vaScript, worden anonieme functies veel gebruikt zonder dat daarvoor een speciaal trefwoord nodig is. In Python worden lambda-ex­pres­sies gebruikt om lokaal kleine functies te maken. Hieronder bespreken we de meest nuttige toe­pas­sin­gen ervan.

Hoe hogere orde functies in Python te vullen met lambda’s

Lambdas worden vaak gebruikt met hogere-orde functies zoals map(), filter() en reduce(). Dankzij lambdas kunnen de elementen van een iterable worden ge­trans­for­meerd zonder gebruik te maken van loops. Hogere-orde functies zijn functies die functies als pa­ra­me­ters nemen of een functie re­tour­ne­ren.

De functie map() neemt een functie en een iterable als pa­ra­me­ters. Het voert de functie uit voor elk element van de iterable. Laten we eens proberen om kwadraten te genereren. We gebruiken de functie map() en geven een lambda-expressie als argument door, die de kwa­draat­func­tie genereert. De kwa­draat­func­tie wordt toegepast op elk element van de lijst met map():

nums = [3, 5, 7]
# Square numbers using `map()` and `lambda`
squares = map(lambda num: num ** 2, nums)
# Show that it works
assert list(squares) == [9, 25, 49]
python
Opmerking

Vanaf Python 3.0 re­tour­ne­ren de functies map() en filter() een iterable in plaats van een lijst. Een list() -aanroep wordt gebruikt binnen de assert sta­te­ments om iterables uit te pakken in een lijst.

Lijst­com­pre­hen­si­ons bieden een modernere be­na­de­ring voor het verwerken van iterables. In plaats van onze toevlucht te nemen tot map() en een lambda-functie te genereren, kunnen we de bewerking direct be­schrij­ven:

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]
python

De functie filter() kan worden gebruikt om de elementen van een iterable te filteren. We kunnen ons voorbeeld uit­brei­den om alleen even kwadraten te genereren:

# 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]
python

We laten zien dat het gebruik van lijst­com­pre­hen­si­on de voorkeur heeft om hetzelfde resultaat te genereren zonder gebruik te maken van lambda’s en hogere-or­de­func­ties. Het if van de com­pre­hen­si­on wordt gebruikt om de even getallen uit de ge­ge­ne­reer­de kwadraten te filteren:

# 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]
python
Opmerking

De functie reduce() van Python is sinds Python 3.0 niet meer opgenomen in de stan­daard­bi­bli­o­theek. Deze functie is te vinden in de module functools.

Hoe je be­lang­rij­ke functies in Python im­ple­men­teert met lambda’s

Com­pre­hen­si­ons hebben de klassieke hogere-orde functies map() en filter() in Python gro­ten­deels vervangen. Er kunnen echter sleu­tel­func­ties worden gebruikt om de volledige kracht van lambda’s te de­mon­stre­ren.

De Python-ver­ge­lij­kings­func­ties sorted(), min() en max() werken op iterables. Elk element van de iterable wordt bij het aanroepen on­der­wor­pen aan een ver­ge­lij­king. De drie functies nemen een sleu­tel­func­tie als optionele parameter key. De sleu­tel­func­tie wordt voor elk element aan­ge­roe­pen en re­tour­neert een sleu­tel­waar­de voor de ver­ge­lij­kings­be­wer­king.

Laten we eens kijken naar het volgende probleem. We hebben een map met af­beel­dings­be­stan­den waarvan de namen zijn toe­ge­we­zen aan een Python-lijst. We willen de lijst sorteren. De be­stands­na­men beginnen allemaal met img, gevolgd door een cijfer:

# List of image file names
images = ['img1', 'img2', 'img30', 'img3', 'img22', 'img100']
python

Als we de functie sorted() van Python gebruiken, wordt de lexi­co­gra­fi­sche volgorde gebruikt. Hierbij worden op­een­vol­gen­de cijfers als af­zon­der­lij­ke getallen behandeld. Daardoor worden de getallen ['1', '2', '100'] in de volgorde ['1', '100', '2'] geplaatst. Het resultaat is niet wat we ver­wach­ten:

# Sort using lexicographic order
sorted_image = sorted(images)
# Show that it works
assert sorted_image == ['img1', 'img100', 'img2', 'img22', 'img3', 'img30']
python

We geven een lambda door die een sleu­tel­func­tie pro­du­ceert om ervoor te zorgen dat het sorteren correct verloopt. De sleu­tel­func­tie haalt het numerieke deel van een be­stands­naam eruit, dat door sorted() als sleutel wordt gebruikt:

# 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']
python

De sleu­tel­func­tie wordt lokaal en slechts één keer gebruikt. Het is niet nodig om hiervoor een extra benoemde functie te de­fi­ni­ë­ren. Lambda’s zijn de juiste manier om sleu­tel­func­ties te maken. Laten we nog twee voor­beel­den bekijken.

Net als sorted() hebben de in­ge­bouw­de Python-functies min() en max() een optionele sleu­tel­func­tie. De functies zoeken het kleinste en grootste element in een lijst of andere iterable. Het kleinste of grootste element is een kwestie van definitie en kan worden ge­spe­ci­fi­ceerd met behulp van de sleu­tel­func­tie.

Het is duidelijk wat wordt bedoeld met het kleinste of grootste element voor lijsten met een­vou­di­ge waarden, zoals een lijst met getallen. In dit geval hebben we geen speciale sleu­tel­func­tie nodig:

nums = [42, 69, 51, 13]
assert min(nums) == 13
assert max(nums) == 69
python
Opmerking

Als er geen sleu­tel­func­tie wordt door­ge­ge­ven, wordt de iden­ti­teits­func­tie f(x) = x als standaard gebruikt. Dit kan eenvoudig worden ge­de­fi­ni­eerd als een Python-lambda met lambda x: x.

Maar wat als elk element van een iterable meerdere datums bevat? Laten we eens kijken naar een lijst met woor­den­boe­ken die mensen met hun namen en leef­tij­den weergeven. Wat is het criterium voor min() en max() bij het bepalen van het kleinste en grootste element? Hier komt een sleu­tel­func­tie goed van pas.

We hebben voor­beeld­ge­ge­vens nodig om te il­lu­stre­ren hoe be­lang­rij­ke functies werken. Laten we een functie Person() maken die als con­struc­tor dient:

# 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}
python

We maken een lijst van personen met behulp van onze con­struc­tor-functie:

# Create list of people
people = [person('Jim', 42), person('Jack', 51), person('John', 69)]
python

We zoeken de oudste persoon met behulp van de max() call. Dit genereert een sleu­tel­func­tie met behulp van de lambda-expressie, die een person dict neemt en daaruit de leeftijd haalt als ver­ge­lij­kings­ele­ment:

# Find the oldest person
oldest = max(people, key=lambda person: person['age'])
# Check that it works
assert oldest == Person('John', 69)
python

De aanpak werkt precies hetzelfde voor de functie min(). In dit geval de­fi­ni­ë­ren we de sleu­tel­func­tie buiten de aanroep min() en gebruiken we opnieuw een lambda-expressie. Dit verbetert de lees­baar­heid en is de moeite waard als de sleu­tel­func­tie meerdere lokale toe­pas­sin­gen heeft:

# 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)
python

Hoe slui­tin­gen maken met Python-lambda’s

Python-lambda’s worden ook gebruikt bij het de­fi­ni­ë­ren van closures. Dit zijn functies die door andere functies worden gemaakt en een waarde opslaan. Closures kunnen worden gebruikt om families van ver­ge­lijk­ba­re functies te maken. We laten een veel­voor­ko­mend voorbeeld zien waarbij machts­func­ties worden gemaakt.

Macht­func­ties nemen een argument en verheffen dit tot een macht. De kwa­dra­ti­sche functie f(x) = x ^ 2 en de kubische functie f(x) = x ^ 3 zijn bekende voor­beel­den. Wil­le­keu­ri­ge macht­func­ties kunnen worden ge­ge­ne­reerd als slui­tin­gen met behulp van een con­struc­tor­func­tie. We gebruiken een lambda-expressie, wat betekent dat we geen interne functie met een naam hoeven te de­fi­ni­ë­ren:

# 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) == 1000
python

Hoe gebruik je een on­mid­del­lijk aan­ge­roe­pen functie-expressie (IIFE) met Python-lambda’s?

IIFE, uit­ge­spro­ken als ‘iffy’, is een bekend patroon in Ja­vaScript. Het houdt in dat een anonieme functie wordt ge­de­fi­ni­eerd en on­mid­del­lijk wordt uit­ge­voerd.

Lambda’s kunnen worden gebruikt als IIFE’s, hoewel ze vanwege be­per­kin­gen in Python niet erg nuttig zijn. We hoeven alleen maar haakjes rond de lambda-expressie te plaatsen:

(lambda num: num * num)
python

En nog een paar haakjes met daarin het argument of de ar­gu­men­ten:

assert (lambda num: num * num)(3) == 9
python
Ga naar hoofdmenu