Lambda-funkt­sioo­nid on olnud funkt­sio­naalse prog­ram­mee­ri­mise meetod alates Python 1.0 ver­sioo­nist. Viimastel aastatel on aga po­pu­laar­sust kogunud teised tehnikad, mis on lambdad suures osas asendanud. Siiski on lamb­da­dele veel mõned spet­sii­fi­li­sed ka­su­tus­vald­kon­nad, mida kogenud Python-prog­ram­mee­ri­jad peaksid teadma.

Mis on lambda-funkt­sioo­nid Pythonis?

Pythonis viitab lambda-funkt­sioon ano­nüüm­sele funkt­sioo­nile. Python kasutab lambda-funkt­siooni loomiseks märksõna lambda. Lambda-väljend koosneb märk­sõ­nast lambda, millele järgneb ar­gu­men­tide loend, koolon ja üksik väljend. Väl­jen­dile antakse ar­gu­men­did ja see hin­na­takse lambda-funkt­siooni kut­su­misel:

lambda argument: expression

Funkt­sioo­nid on peaaegu kõigi prog­ram­mee­ri­mis­keelte põ­hi­konst­rukt­sioo­nid ja need kujutavad endast väik­sei­mat taas­ka­su­ta­ta­vat koo­di­ühi­kut. Ta­va­li­selt mää­rat­le­takse funkt­sioo­nid Pythonis märk­sõ­naga def. Näitame teile näiteena ruut­funkt­siooni, mis korrutab arvu iseendaga:

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

def märksõna on tuntud viis funkt­sioo­nide mää­rat­le­miseks Pythonis, kuid keeles on ka lambdad. Need on ano­nüüm­sed funkt­sioo­nid, mis mää­rat­le­vad pa­ra­meet­ri­tega avaldise. Lambdasid saab kasutada kõikjal, kus oodatakse funkt­siooni või kus neid saab nimele omistada. Siin näete ruut­funkt­sioo­niga võrd­väär­set lambda-avaldist:

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

Mis vahe on lambda ja def vahel?

Võib tunduda veider, et Python võimaldab luua funkt­sioone nii lambda kui ka def abil. Siiski ei ole Lambda omaette funkt­sioon, vaid lihtsalt veel üks viis lühikeste funkt­sioo­nide loomiseks ko­ha­li­kult. Iga lambda abil loodud funkt­sioon on võimalik luua ka def abil. Siiski ei kehti see vastupidi.

Sün­tak­ti­li­sel tasandil on lambda ja def mõlemad võt­me­sõ­nad. Üks oluline erinevus nende vahel on Pythoni range lause ja avaldise eral­da­mine. Laused on koodi täitmise etapid, avaldised aga väär­tu­seks hin­da­misel.

Def algab avaldus, täpsemalt lii­ta­val­dus, mis sisaldab täien­da­vaid avaldusi. def avalduses võib esineda ainult return avaldust. return avaldus tagastab väärtuse, kui kut­su­takse def mää­rat­le­tud funkt­siooni.

Erinevalt def lausest algablambda väl­jen­diga, mis ei tohi sisaldada ühtegi lauset. Lambda-väljend võtab vastu ühe või mitu argumenti ja tagastab anonüümse funkt­siooni. Kui lambda-funkt­sioon kut­su­takse, hin­na­takse selles sisalduv väljend edastatud ar­gu­men­ti­dega ja see ta­gas­ta­takse.

Millised on Pythoni lambda-väl­jen­dite piirangud?

Python on tead­li­kult piiranud lambda-funkt­sioo­nide ka­su­lik­kust, kuna ta­va­li­selt on parem funkt­sioo­ni­dele nimed anda. See sunnib prog­ram­mee­ri­jaid mõtlema funkt­siooni tähenduse üle ja eristama selgelt selle osi.

Lambdad ei saa sisaldada juhiseid, erinevalt def märk­sõ­naga mää­rat­le­tud funkt­siooni kehast. Seetõttu ei ole võimalik kasutada if, for jne lambda-funkt­sioo­nis. Samuti ei ole võimalik käivitada erandit, kuna selleks on vaja raise avaldust.

Pythonis võivad lambda-funkt­sioo­nid sisaldada ühte avaldist, mis hin­na­takse funkt­siooni kut­su­misel. Lambda-avaldises ei saa kasutada tüübi märkusi. Tä­na­päe­val ka­su­ta­takse Pythonis lambda-funkt­sioo­nide enamikus ka­su­ta­mis­juh­tu­des teisi tehnikaid, näiteks arusaamu.

Lambda-funkt­sioo­nide erinevad ka­su­tus­vii­sid Pythonis

Lambdad on tuletatud funkt­sio­naal­sest prog­ram­mee­ri­mi­sest. Mõnes keeles, näiteks Ja­vaSc­rip­tis, ka­su­ta­takse ano­nüüm­seid funkt­sioone laial­da­selt ilma spet­siaalse märksõna va­ja­du­seta. Pythonis ka­su­ta­takse lambda-väl­jen­deid väikeste funkt­sioo­nide loomiseks ko­ha­li­kult. Allpool vaatame läbi nende kõige ka­su­li­ku­mad ra­ken­dused.

Kuidas täita Pythoni kõrgemate jär­je­kor­dade funkt­sioone lamb­da­dega

Lambdasid ka­su­ta­takse sageli kõrgemate jär­je­kor­dade funkt­sioo­ni­dega, nagu map(), filter() ja reduce(). Lambdade abil saab iterable’i elemente tei­sen­dada ilma tsükleid ka­su­ta­mata. Kõrgemate jär­je­kor­dade funkt­sioo­nid on funkt­sioo­nid, mis võtavad pa­ra­meet­ri­tena funkt­sioone või ta­gas­ta­vad funkt­siooni.

Funkt­sioon map() võtab pa­ra­meet­ri­tena vastu funkt­siooni ja iterable’i. See käivitab funkt­siooni iga iterable’i elemendi jaoks. Proovime ge­ne­ree­rida ruut­numb­reid. Kasutame funkt­siooni map() ja edastame ar­gu­men­dina lambda-väljendi, mis ge­ne­ree­rib ruut­funkt­siooni. Ruut­funkt­siooni ra­ken­da­takse iga loendi elemendi jaoks map() abil:

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
Note

Alates Python 3.0 ver­sioo­nist ta­gas­ta­vad funkt­sioo­nid map() ja filter() loendi asemel iterable’i. list() kut­su­takse assert aval­dis­tes, et iterable’id loendisse lahti pakkida.

Loendite mõistmine pakub kaas­aeg­se­mat lä­he­ne­mis­viisi iterable’ide tööt­le­miseks. Selle asemel, et kasutada map() ja luua lambda-funkt­sioon, saame kir­jel­dada ope­rat­siooni otse:

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

Funkt­siooni filter() abil saab filt­ree­rida iterable’i elemente. Me võime laiendada oma näidet, et ge­ne­ree­rida ainult paa­ris­ar­vu­lisi ruut­numb­reid:

# 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

Näitame eelis­ta­tud lä­he­ne­mis­viisi, mille puhul ka­su­ta­takse loendi mõistmist, et saada sama tulemus ilma lambdade ja kõrgemate järku funkt­sioo­nide ka­su­ta­miseta. Mõistmise osa if ka­su­ta­takse, et filt­ree­rida ge­ne­ree­ri­tud ruut­numb­rite hulgast välja paa­ris­numb­rid:

# 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
Note

Pythoni funkt­sioon reduce() ei ole alates Python 3.0 ver­sioo­nist stan­dard­tee­gis si­sal­du­nud. See funkt­sioon on leitav moodulis functools.

Kuidas rakendada Pythonis lambdade abil põhilisi funkt­sioone

Comp­re­hen­sions on Pythonis suures osas asendanud klas­si­ka­li­sed kõrgemate jär­je­kor­dade funkt­sioo­nid map() ja filter(). Siiski saab lambdade kõiki eeliseid de­monst­ree­rida võt­me­funkt­sioo­nide abil.

Pythoni võrd­lus­funkt­sioo­nid sorted(), min() ja max() töötavad iterable’itega. Iga iterable’i element võr­rel­dakse, kui funkt­sioon kut­su­takse. Need kolm funkt­siooni võtavad võt­me­funkt­siooni va­li­ku­lise pa­ra­meet­rina key. Võt­me­funkt­sioon kut­su­takse iga elemendi jaoks ja see tagastab võrd­lusope­rat­siooni jaoks võt­me­väär­tuse.

Vaatleme järgmist probleemi. Meil on kaust pil­di­fai­li­dega, mille nimed on seotud Python-ni­me­kir­jaga. Soovime nimekirja sor­tee­rida. Kõik failini­med algavad numbriga img, millele järgneb number:

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

Kui kasutame Pythoni funkt­siooni sorted(), ka­su­ta­takse lek­si­kog­raa­fi­list jär­jes­tust. See käsitleb jär­jes­ti­ku­seid numbreid ühe numbrina. Seega pai­gu­ta­takse numbrid ['1', '2', '100'] jär­jes­tusse ['1', '100', '2']. Tulemus ei ole selline, nagu ootame:

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

Me edastame lambda väljendi, mis loob võt­me­funkt­siooni, et tagada sor­tee­ri­mise õigsus. Võt­me­funkt­sioon eraldab failinime numb­ri­lise osa, mida sorted() kasutab võtmena:

# 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

Võt­me­funkt­siooni ka­su­ta­takse ko­ha­li­kult ja ainult üks kord. Selle jaoks pole vaja mää­rat­leda eraldi nimega funkt­siooni. Lambdad on õige viis võt­me­funkt­sioo­nide loomiseks. Vaadakem veel kahte näidet.

Nagu sorted(), võtavad sis­se­ehi­ta­tud Python-funkt­sioo­nid min() ja max() vastu va­li­ku­lise võt­me­funkt­siooni. Funkt­sioo­nid leiavad ni­me­kir­jast või muust ite­ra­tiiv­se­test ele­men­ti­dest väikseima ja suurima elemendi. Väikseim või suurim element on mää­rat­luse küsimus ja seda saab täp­sus­tada võt­me­funkt­siooni abil.

Lihtsate väärtuste loendite puhul, näiteks numbrite loendite puhul, on selge, mida tähendab väikseim või suurim element. Sel juhul ei ole vaja spet­siaal­set võt­me­funkt­siooni:

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

Kui võt­me­funkt­siooni ei edastata, ka­su­ta­takse vaikimisi iden­ti­teedi­funkt­siooni f(x) = x. Seda saab hõlpsasti de­fi­nee­rida Python lambda abil lambda x: x.

Aga mis siis, kui iterable’i iga element sisaldab mitut kuupäeva? Ku­jut­le­gem nimekirja, mis sisaldab inimeste nimesid ja vanuseid. Millised on kri­tee­riu­mid min() ja max() puhul, kui ot­sus­ta­takse, milline on väikseim ja suurim element? Siin on kasulik võt­me­funkt­sioon.

Vajame näi­te­and­meid, et il­lust­ree­rida võt­me­funkt­sioo­nide töö­põ­hi­mõ­tet. Loome funkt­siooni Person(), mis toimib konst­ruk­to­rina:

# 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

Me loome inimeste nimekirja, kasutades oma konst­ruk­to­ri­funkt­siooni:

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

Leiame vanima isiku, kasutades max() kõnet. See ge­ne­ree­rib võt­me­funkt­siooni, kasutades lambda-väljendit, mis võtab isiku sõnastiku ja ekst­rak­tib sellest vanuse võrd­lu­s­e­le­men­dina:

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

Sama lä­he­ne­mis­viis toimib ka funkt­siooni min() puhul. Sel juhul mää­rat­leme võt­me­funkt­siooni väl­jas­pool min() kutsumist ja kasutame taas lambda-väljendit. See parandab loetavust ja on kasulik, kui võt­me­funkt­sioo­nil on mitu kohalikku kasutust:

# 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

Kuidas luua sulgureid Python lambdas

Python lambdas ka­su­ta­takse ka sulgude mää­rat­le­misel. Need on funkt­sioo­nid, mis on loodud teiste funkt­sioo­nide poolt ja sal­ves­ta­vad väärtuse. Sulgude abil saab luua sarnaste funkt­sioo­nide perekondi. Näitame tavalist näidet, kus luuakse võim­sus­funkt­sioo­nid.

Võim­sus­funkt­sioo­nid võtavad argumendi ja tõstavad selle eks­po­nen­diks. Ruut­funkt­sioon f(x) = x ^ 2 ja kuup­funkt­sioon f(x) = x ^ 3 on tuntud näited. Konst­ruk­tor­funkt­siooni abil saab luua suvalisi võim­sus­funkt­sioone kui sulgumisi. Kasutame lambda-väljendit, mis tähendab, et meil ei ole vaja mää­rat­leda sisemist nimelist funkt­siooni:

# 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

Kuidas kasutada Python lambdas kohe käi­vi­ta­ta­vat funkt­siooni väljendit (IIFE)

IIFE, hääl­da­takse „iffy”, on Ja­vaSc­rip­tis tuntud muster. See hõlmab anonüümse funkt­siooni mää­rat­le­mist ja selle kohest täitmist.

Lambdasid saab kasutada IIFE-dena, kuigi need ei ole Pythoni piiran­gute tõttu eriti kasulikud. Me peame ainult lambda-väljendi ümber sul­gu­desse panema:

(lambda num: num * num)
python

Ja veel üks sulgude paar, mis sisaldab argumenti (argumente):

assert (lambda num: num * num)(3) == 9
python
Go to Main Menu