Lambda funkcije so bile metoda za funk­ci­o­nal­no pro­gra­mi­ra­nje že od Python 1.0. Vendar pa so v zadnjih letih druge tehnike pridobile večjo pri­lju­blje­nost in v veliki meri na­do­me­sti­le lambda funkcije. Kljub temu pa obstajajo še vedno nekatere spe­ci­a­li­zi­ra­ne uporabe lambda funkcij, ki jih morajo poznati izkušeni Python pro­gra­mer­ji.

Kaj so lambda funkcije v Pythonu?

V Pythonu se lambda funkcija nanaša na anonimno funkcijo. Python uporablja ključno besedo lambda za ustvar­ja­nje lambda funkcije. Lambda izraz je se­sta­vljen iz ključne besede lambda, ki ji sledi seznam ar­gu­men­tov, dvopičje in en izraz. Izraz je opremljen z argumenti in se izračuna, ko se pokliče lambda funkcija:

lambda argument: expression

Funkcije so temeljna jezikovna kon­struk­ci­ja v skoraj vseh pro­gram­skih jezikih in pred­sta­vlja­jo najmanjšo ponovno uporabno enoto kode. Običajno so funkcije v Pythonu de­fi­ni­ra­ne s ključno besedo def. Kot primer vam bomo pokazali kvadratno funkcijo, ki pomnoži število s samim sabo:

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

Ključna beseda def je znan način za de­fi­ni­ra­nje funkcij v Pythonu, vendar jezik pozna tudi lambda izraze. To so anonimne funkcije, ki de­fi­ni­ra­jo izraz s parametri. Lambda izrazi se lahko upo­ra­blja­jo povsod, kjer se pričakuje funkcija ali kjer se lahko dodelijo imenu. Tukaj si lahko ogledate lambda izraz, ki je ena­ko­vre­den kvadratni funkciji:

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

Kakšna je razlika med lambda in def?

Morda se zdi čudno, da Python omogoča ustvar­ja­nje funkcij tako z lambda kot z def. Vendar Lambda ni njegova lastna funkcija, ampak le še en način za lokalno ustvar­ja­nje kratkih funkcij. Vsaka funkcija, ustvar­je­na z lambda, se lahko ustvari tudi z def. Vendar pa to ne velja v obratni smeri.

Na sin­tak­tič­ni ravni sta lambda in def ključni besedi. Ena ključna razlika med njima je stroga ločitev izjav in izrazov v Pythonu. Izjave so koraki pri izvajanju kode, medtem ko se izrazi ovre­dno­ti­jo v vrednost.

Def začne izjavo, na­tanč­ne­je se­sta­vlje­no izjavo, ki vsebuje nadaljnje izjave. V izjavi def se lahko pojavi samo return izjava. Izjava return vrne vrednost, ko se pokliče funkcija, de­fi­ni­ra­na z def.

Za razliko od izjave def, lambda začne izraz, ki ne more vsebovati nobenih izjav. Lambda izraz sprejme enega ali več ar­gu­men­tov in vrne anonimno funkcijo. Ko se lambda funkcija pokliče, se izraz, ki je v njej, ovrednoti s pre­ne­se­ni­mi argumenti in se vrne.

Kakšne so omejitve lambda izrazov v Pythonu?

Python je namerno omejil upo­rab­nost lambda funkcij, saj je običajno bolje po­i­me­no­va­ti funkcije. To pro­gra­mer­je prisili, da raz­mi­sli­jo o pomenu funkcije in jasno raz­li­ku­je­jo med po­sa­me­zni­mi deli.

Lambda izrazi ne morejo vsebovati navodil, za razliko od telesa funkcije, de­fi­ni­ra­ne s ključno besedo def. Zato v lambda funkciji ni mogoče uporabiti if, for itd. Prav tako ni mogoče sprožiti izjeme, saj je za to potrebna izjava raise.

Lambda funkcije v Pythonu lahko vsebujejo en sam izraz, ki se izračuna ob klicu. Tipovne opombe se ne morejo upo­ra­blja­ti znotraj lambda izraza. Danes večina primerov uporabe lambda funkcij v Pythonu uporablja druge tehnike, kot so ra­zu­me­va­nja.

Različne uporabe lambda funkcij v Pythonu

Lambda izhaja iz funk­ci­o­nal­ne­ga pro­gra­mi­ra­nja. V nekaterih jezikih, kot je Ja­va­Script, se anonimne funkcije pogosto upo­ra­blja­jo brez potrebe po posebni ključni besedi. V Pythonu se lambda izrazi upo­ra­blja­jo za lokalno ustvar­ja­nje majhnih funkcij. V na­da­lje­va­nju bomo pre­gle­da­li njihove najbolj uporabne apli­ka­ci­je.

Kako v Pythonu napolniti funkcije višjega reda z lambda izrazi

Lambde se pogosto upo­ra­blja­jo z višjimi funk­ci­ja­mi, kot so map(), filter() in reduce(). Elementi ite­ra­bil­ne­ga niza se lahko pre­o­bli­ku­je­jo brez uporabe zank, za­hva­lju­joč lambdam. Višje funkcije so funkcije, ki spre­je­ma­jo funkcije kot parametre ali vrnejo funkcijo.

Funkcija map() sprejme funkcijo in ite­ra­bil­no kot parametre. Izvede funkcijo za vsak element ite­ra­bil­ne­ga. Poskusimo ustvariti kvadratne številke. Uporabimo funkcijo map() in kot argument prenesemo lambda izraz, ki ustvari kvadratno funkcijo. Kvadratna funkcija se uporabi za vsak element seznama s 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
Note

Od Python 3.0 naprej funkcije map() in filter() vrnejo ite­ra­bil­no namesto seznama. Klic list() se uporablja znotraj izjav assert za raz­pa­ki­ra­nje ite­ra­bil­nih v seznam.

Seznami ra­zu­me­vanj ponujajo so­dob­nej­ši pristop za obdelavo ite­ra­bil­nih elementov. Namesto da se zatečemo k map() in ustvarimo lambda funkcijo, lahko operacijo opišemo ne­po­sre­dno:

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

Funkcijo filter() lahko uporabimo za fil­tri­ra­nje elementov ite­ra­bil­ne­ga niza. Naš primer lahko razširimo tako, da ge­ne­ri­ra­mo samo sode kvadratne številke:

# 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

Pri­ka­zu­je­mo pred­no­stni pristop uporabe ra­zu­me­va­nja seznama za ustvar­ja­nje enakega rezultata brez uporabe lambd in funkcij višjega reda. Del ra­zu­me­va­nja if se uporablja za fil­tri­ra­nje sodeštev iz ustvar­je­nih kva­dra­tnih števil:

# 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

Funkcija Python reduce() ni bila vključena v stan­dar­dno knjižnico od Python 3.0. To funkcijo lahko najdete v modulu functools.

Kako im­ple­men­ti­ra­ti ključne funkcije v Pythonu z lambdami

Ra­zu­me­va­nja so v veliki meri na­do­me­sti­la klasične funkcije višjega reda map() in filter() v Pythonu. Vendar pa se lahko ključne funkcije uporabijo za pri­ka­zo­va­nje vseh prednosti lambd.

Pythonove pri­mer­jal­ne funkcije sorted(), min() in max() delujejo na ite­ra­bil­nih objektih. Vsak element ite­ra­bil­ne­ga objekta je ob klicu predmet pri­mer­ja­ve. Tri funkcije spre­je­ma­jo ključno funkcijo kot neobvezni parameter key. Ključna funkcija se pokliče za vsak element in vrne ključno vrednost za pri­mer­jal­no operacijo.

Raz­mi­sli­mo o na­sle­dnjem problemu. Imamo mapo s sli­kov­ni­mi da­to­te­ka­mi, katerih imena so dodeljena seznamu v Pythonu. Seznam želimo raz­vr­sti­ti. Vsa imena datotek se začnejo s img, ki ji sledi številka:

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

Če uporabimo funkcijo sorted() v Pythonu, se uporabi le­ksi­ko­graf­ski red. Ta zaporedne številke obravnava kot eno samo število. Tako se številke ['1', '2', '100'] uvrstijo v red ['1', '100', '2']. Rezultat ni tak, kot smo pri­ča­ko­va­li:

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

Prehodimo izraz lambda, ki ustvari ključno funkcijo, da za­go­to­vi­mo pravilno raz­vr­šča­nje. Ključna funkcija izvleče številčni del imena datoteke, ki se uporabi kot ključ v sorted():

# 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

Ključna funkcija se uporablja lokalno in samo enkrat. Ni potrebno, da zanjo opre­de­li­te dodatno imenovano funkcijo. Lambda izrazi so pravi način za ustvar­ja­nje ključnih funkcij. Poglejmo si še dva primera.

Podobno kot sorted(), vgrajeni Pythonovi funkciji min() in max() spre­je­ma­ta neobvezno ključno funkcijo. Funkciji poiščeta najmanjši in največji element v seznamu ali drugem ite­ra­bil­nem objektu. Najmanjši ali največji element je stvar de­fi­ni­ci­je in ga je mogoče določiti s ključno funkcijo.

Jasno je, kaj pomeni najmanjši ali največji element za sezname pre­pro­stih vrednosti, kot je seznam številk. V tem primeru ne po­tre­bu­je­mo posebne ključne funkcije:

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

Če ni prenesena nobena ključna funkcija, se kot privzeta uporabi iden­ti­te­ta funkcija f(x) = x. To se lahko enostavno definira kot Python lambda z lambda x: x.

Kaj pa, če vsak element ite­ra­bil­ne­ga niza vsebuje več datumov? Pred­sta­vljaj­mo si seznam slovarjev, ki pred­sta­vlja­jo ljudi z njihovimi imeni in starostmi. Kakšno je merilo za min() in max() pri odločanju, kateri je najmanjši in kateri največji element? Tukaj pride v poštev ključna funkcija.

Po­tre­bu­je­mo vzorčne podatke, da po­na­zo­ri­mo delovanje ključnih funkcij. Ustvarimo funkcijo Person(), ki služi kot kon­struk­tor:

# 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

Seznam oseb ustvarimo s pomočjo naše kon­struk­tor­ske funkcije:

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

Najdemo naj­sta­rej­šo osebo z uporabo klica max(). To ustvari ključno funkcijo z uporabo lambda izraza, ki vzame slovar oseb in iz njega izvleče starost kot pri­mer­jal­ni element:

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

Pristop deluje popolnoma enako za funkcijo min(). V tem primeru bomo ključno funkcijo de­fi­ni­ra­li zunaj klica min() in ponovno uporabili lambda izraz. To izboljša ber­lji­vost in je koristno, če ima ključna funkcija več lokalnih uporab:

# 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

Kako ustvariti zapiranja s Pytho­no­vi­mi lambdami

Pythonove lambda funkcije se upo­ra­blja­jo tudi pri de­fi­ni­ra­nju zapiranj. To so funkcije, ki jih ustvarijo druge funkcije in shra­nju­je­jo vrednost. Zapiranja se lahko uporabijo za ustvar­ja­nje družin podobnih funkcij. Pokazali bomo pogost primer, kjer se ustvar­ja­jo potenčne funkcije.

Funkcije potence sprejmejo argument in ga ek­spo­nen­ti­ra­jo. Funkcija kvadrata f(x) = x ^ 2 in funkcija kubika f(x) = x ^ 3 sta dobro znana primera. Poljubne funkcije potence lahko ustvarimo kot zaprtje s pomočjo kon­struk­tor­ske funkcije. Uporabili bomo lambda izraz, kar pomeni, da nam ni treba de­fi­ni­ra­ti notranje imenovane funkcije:

# 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

Kako upo­ra­blja­ti takojšnjo izraženo funkcijo (IIFE) z Pytho­no­vi­mi lambdami

IIFE, iz­go­vor­je­no kot „iffy“, je znan vzorec v Ja­va­Scrip­tu. Vključuje de­fi­ni­ra­nje anonimne funkcije in njeno takojšnjo izvedbo.

Lambde se lahko upo­ra­blja­jo kot IIFE, čeprav niso zelo uporabne zaradi omejitev v Pythonu. Lambda izraz moramo samo obdati z oklepaji:

(lambda num: num * num)
python

In še en par oklepajev, ki vsebujejo argument(e):

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