Hoe lambda-functies te gebruiken in Python
Lambda-functies worden al sinds Python 1.0 gebruikt voor functioneel programmeren. De laatste jaren zijn er echter andere technieken populairder geworden, die lambda’s grotendeels hebben vervangen. Toch zijn er nog steeds een aantal gespecialiseerde toepassingen voor lambda’s die ervaren Python-programmeurs moeten kennen.
Wat zijn lambda-functies in Python?
In Python verwijst een lambda-functie naar een anonieme functie. Python gebruikt het sleutelwoord lambda om een lambda-functie te maken. Een lambda-expressie bestaat uit het sleutelwoord lambda, gevolgd door een lijst met argumenten, een dubbele punt en een enkele expressie. De expressie wordt voorzien van de argumenten en geëvalueerd wanneer de lambda-functie wordt aangeroepen:
lambda argument: expressionFuncties zijn een fundamenteel taalconstruct in bijna elke programmeertaal en vormen de kleinste herbruikbare eenheid van code. Functies in Python worden doorgaans gedefinieerd met het sleutelwoord def. Als voorbeeld laten we u de kwadratische functie zien, die een getal met zichzelf vermenigvuldigt:
# Define square function
def square(num):
return num * num
# Show that it works
assert square(9) == 81pythonHet sleutelwoord def is een bekende manier om functies in Python te definiëren, maar de taal heeft ook lambda’s. Dit zijn anonieme functies die een uitdrukking met parameters definiëren. Lambda’s kunnen overal worden gebruikt waar een functie wordt verwacht of aan een naam kan worden toegewezen. Hier ziet u de lambda-uitdrukking die gelijk is aan de kwadratische functie:
# Create square function
squared = lambda num: num * num
# Show that it works
assert squared(9) == 81pythonWat 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 syntactisch niveau zijn lambda en def beide sleutelwoorden. Een belangrijk verschil tussen beide is de strikte scheiding tussen statements en expressies in Python. Statements zijn stappen in de uitvoering van code, terwijl expressies worden geëvalueerd tot een waarde.
Def begint een statement, of meer specifiek een samengesteld statement, dat verdere statements bevat. Er mogen alleen return statements voorkomen binnen een def statement. Een return statement retourneert een waarde wanneer de functie gedefinieerd met def wordt aangeroepen.
In tegenstelling tot de def -instructie begintlambda een expressie die geen instructies mag bevatten. De lambda-expressie neemt een of meer argumenten en retourneert een anonieme functie. Wanneer de lambda-functie wordt aangeroepen, wordt de expressie die erin staat geëvalueerd met de doorgegeven argumenten en wordt deze geretourneerd.
Wat zijn de beperkingen van Python’s lambda-expressies?
Python heeft het nut van lambda-functies bewust beperkt, omdat het meestal beter is om functies een naam te geven. Dit dwingt programmeurs om na te denken over de betekenis van de functie en om onderdelen duidelijk te onderscheiden.
Lambdas kunnen geen instructies bevatten, in tegenstelling tot de body van een functie die is gedefinieerd met het sleutelwoord def. Het is daarom niet mogelijk om if, for, enz. te gebruiken in een lambda-functie. Het is ook niet mogelijk om een uitzondering te activeren, omdat hiervoor een raise nodig is.
Lambda-functies in Python kunnen één enkele expressie bevatten die wordt geëvalueerd wanneer deze wordt aangeroepen. Type-annotaties kunnen niet worden gebruikt binnen de lambda-expressie. Tegenwoordig maken de meeste gebruiksscenario’s van lambda-functies in Python gebruik van andere technieken, zoals comprehensions.
Verschillende toepassingen voor lambda-functies in Python
Lambda’s zijn afgeleid van functioneel programmeren. In sommige talen, zoals JavaScript, worden anonieme functies veel gebruikt zonder dat daarvoor een speciaal trefwoord nodig is. In Python worden lambda-expressies gebruikt om lokaal kleine functies te maken. Hieronder bespreken we de meest nuttige toepassingen 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 getransformeerd zonder gebruik te maken van loops. Hogere-orde functies zijn functies die functies als parameters nemen of een functie retourneren.
De functie map() neemt een functie en een iterable als parameters. 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 kwadraatfunctie genereert. De kwadraatfunctie 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]pythonVanaf Python 3.0 retourneren de functies map() en filter() een iterable in plaats van een lijst. Een list() -aanroep wordt gebruikt binnen de assert statements om iterables uit te pakken in een lijst.
Lijstcomprehensions bieden een modernere benadering 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 beschrijven:
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]pythonDe functie filter() kan worden gebruikt om de elementen van een iterable te filteren. We kunnen ons voorbeeld uitbreiden 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]pythonWe laten zien dat het gebruik van lijstcomprehension de voorkeur heeft om hetzelfde resultaat te genereren zonder gebruik te maken van lambda’s en hogere-ordefuncties. Het if van de comprehension wordt gebruikt om de even getallen uit de gegenereerde 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]pythonDe functie reduce() van Python is sinds Python 3.0 niet meer opgenomen in de standaardbibliotheek. Deze functie is te vinden in de module functools.
Hoe je belangrijke functies in Python implementeert met lambda’s
Comprehensions hebben de klassieke hogere-orde functies map() en filter() in Python grotendeels vervangen. Er kunnen echter sleutelfuncties worden gebruikt om de volledige kracht van lambda’s te demonstreren.
De Python-vergelijkingsfuncties sorted(), min() en max() werken op iterables. Elk element van de iterable wordt bij het aanroepen onderworpen aan een vergelijking. De drie functies nemen een sleutelfunctie als optionele parameter key. De sleutelfunctie wordt voor elk element aangeroepen en retourneert een sleutelwaarde voor de vergelijkingsbewerking.
Laten we eens kijken naar het volgende probleem. We hebben een map met afbeeldingsbestanden waarvan de namen zijn toegewezen aan een Python-lijst. We willen de lijst sorteren. De bestandsnamen beginnen allemaal met img, gevolgd door een cijfer:
# List of image file names
images = ['img1', 'img2', 'img30', 'img3', 'img22', 'img100']pythonAls we de functie sorted() van Python gebruiken, wordt de lexicografische volgorde gebruikt. Hierbij worden opeenvolgende cijfers als afzonderlijke getallen behandeld. Daardoor worden de getallen ['1', '2', '100'] in de volgorde ['1', '100', '2'] geplaatst. Het resultaat is niet wat we verwachten:
# Sort using lexicographic order
sorted_image = sorted(images)
# Show that it works
assert sorted_image == ['img1', 'img100', 'img2', 'img22', 'img3', 'img30']pythonWe geven een lambda door die een sleutelfunctie produceert om ervoor te zorgen dat het sorteren correct verloopt. De sleutelfunctie haalt het numerieke deel van een bestandsnaam 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']pythonDe sleutelfunctie wordt lokaal en slechts één keer gebruikt. Het is niet nodig om hiervoor een extra benoemde functie te definiëren. Lambda’s zijn de juiste manier om sleutelfuncties te maken. Laten we nog twee voorbeelden bekijken.
Net als sorted() hebben de ingebouwde Python-functies min() en max() een optionele sleutelfunctie. 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 gespecificeerd met behulp van de sleutelfunctie.
Het is duidelijk wat wordt bedoeld met het kleinste of grootste element voor lijsten met eenvoudige waarden, zoals een lijst met getallen. In dit geval hebben we geen speciale sleutelfunctie nodig:
nums = [42, 69, 51, 13]
assert min(nums) == 13
assert max(nums) == 69pythonAls er geen sleutelfunctie wordt doorgegeven, wordt de identiteitsfunctie f(x) = x als standaard gebruikt. Dit kan eenvoudig worden gedefinieerd 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 woordenboeken die mensen met hun namen en leeftijden weergeven. Wat is het criterium voor min() en max() bij het bepalen van het kleinste en grootste element? Hier komt een sleutelfunctie goed van pas.
We hebben voorbeeldgegevens nodig om te illustreren hoe belangrijke functies werken. Laten we een functie Person() maken die als constructor 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}pythonWe maken een lijst van personen met behulp van onze constructor-functie:
# Create list of people
people = [person('Jim', 42), person('Jack', 51), person('John', 69)]pythonWe zoeken de oudste persoon met behulp van de max() call. Dit genereert een sleutelfunctie met behulp van de lambda-expressie, die een person dict neemt en daaruit de leeftijd haalt als vergelijkingselement:
# Find the oldest person
oldest = max(people, key=lambda person: person['age'])
# Check that it works
assert oldest == Person('John', 69)pythonDe aanpak werkt precies hetzelfde voor de functie min(). In dit geval definiëren we de sleutelfunctie buiten de aanroep min() en gebruiken we opnieuw een lambda-expressie. Dit verbetert de leesbaarheid en is de moeite waard als de sleutelfunctie meerdere lokale toepassingen 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)pythonHoe sluitingen maken met Python-lambda’s
Python-lambda’s worden ook gebruikt bij het definiëren van closures. Dit zijn functies die door andere functies worden gemaakt en een waarde opslaan. Closures kunnen worden gebruikt om families van vergelijkbare functies te maken. We laten een veelvoorkomend voorbeeld zien waarbij machtsfuncties worden gemaakt.
Machtfuncties nemen een argument en verheffen dit tot een macht. De kwadratische functie f(x) = x ^ 2 en de kubische functie f(x) = x ^ 3 zijn bekende voorbeelden. Willekeurige machtfuncties kunnen worden gegenereerd als sluitingen met behulp van een constructorfunctie. We gebruiken een lambda-expressie, wat betekent dat we geen interne functie met een naam hoeven te definië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) == 1000pythonHoe gebruik je een onmiddellijk aangeroepen functie-expressie (IIFE) met Python-lambda’s?
IIFE, uitgesproken als ‘iffy’, is een bekend patroon in JavaScript. Het houdt in dat een anonieme functie wordt gedefinieerd en onmiddellijk wordt uitgevoerd.
Lambda’s kunnen worden gebruikt als IIFE’s, hoewel ze vanwege beperkingen in Python niet erg nuttig zijn. We hoeven alleen maar haakjes rond de lambda-expressie te plaatsen:
(lambda num: num * num)pythonEn nog een paar haakjes met daarin het argument of de argumenten:
assert (lambda num: num * num)(3) == 9python