Kuidas kasutada lambda-funktsioone Pythonis
Lambda-funktsioonid on olnud funktsionaalse programmeerimise meetod alates Python 1.0 versioonist. Viimastel aastatel on aga populaarsust kogunud teised tehnikad, mis on lambdad suures osas asendanud. Siiski on lambdadele veel mõned spetsiifilised kasutusvaldkonnad, mida kogenud Python-programmeerijad peaksid teadma.
Mis on lambda-funktsioonid Pythonis?
Pythonis viitab lambda-funktsioon anonüümsele funktsioonile. Python kasutab lambda-funktsiooni loomiseks märksõna lambda. Lambda-väljend koosneb märksõnast lambda, millele järgneb argumentide loend, koolon ja üksik väljend. Väljendile antakse argumendid ja see hinnatakse lambda-funktsiooni kutsumisel:
lambda argument: expressionFunktsioonid on peaaegu kõigi programmeerimiskeelte põhikonstruktsioonid ja need kujutavad endast väikseimat taaskasutatavat koodiühikut. Tavaliselt määratletakse funktsioonid Pythonis märksõnaga def. Näitame teile näiteena ruutfunktsiooni, mis korrutab arvu iseendaga:
# Define square function
def square(num):
return num * num
# Show that it works
assert square(9) == 81pythondef märksõna on tuntud viis funktsioonide määratlemiseks Pythonis, kuid keeles on ka lambdad. Need on anonüümsed funktsioonid, mis määratlevad parameetritega avaldise. Lambdasid saab kasutada kõikjal, kus oodatakse funktsiooni või kus neid saab nimele omistada. Siin näete ruutfunktsiooniga võrdväärset lambda-avaldist:
# Create square function
squared = lambda num: num * num
# Show that it works
assert squared(9) == 81pythonMis vahe on lambda ja def vahel?
Võib tunduda veider, et Python võimaldab luua funktsioone nii lambda kui ka def abil. Siiski ei ole Lambda omaette funktsioon, vaid lihtsalt veel üks viis lühikeste funktsioonide loomiseks kohalikult. Iga lambda abil loodud funktsioon on võimalik luua ka def abil. Siiski ei kehti see vastupidi.
Süntaktilisel tasandil on lambda ja def mõlemad võtmesõnad. Üks oluline erinevus nende vahel on Pythoni range lause ja avaldise eraldamine. Laused on koodi täitmise etapid, avaldised aga väärtuseks hindamisel.
Def algab avaldus, täpsemalt liitavaldus, mis sisaldab täiendavaid avaldusi. def avalduses võib esineda ainult return avaldust. return avaldus tagastab väärtuse, kui kutsutakse def määratletud funktsiooni.
Erinevalt def lausest algablambda väljendiga, mis ei tohi sisaldada ühtegi lauset. Lambda-väljend võtab vastu ühe või mitu argumenti ja tagastab anonüümse funktsiooni. Kui lambda-funktsioon kutsutakse, hinnatakse selles sisalduv väljend edastatud argumentidega ja see tagastatakse.
Millised on Pythoni lambda-väljendite piirangud?
Python on teadlikult piiranud lambda-funktsioonide kasulikkust, kuna tavaliselt on parem funktsioonidele nimed anda. See sunnib programmeerijaid mõtlema funktsiooni tähenduse üle ja eristama selgelt selle osi.
Lambdad ei saa sisaldada juhiseid, erinevalt def märksõnaga määratletud funktsiooni kehast. Seetõttu ei ole võimalik kasutada if, for jne lambda-funktsioonis. Samuti ei ole võimalik käivitada erandit, kuna selleks on vaja raise avaldust.
Pythonis võivad lambda-funktsioonid sisaldada ühte avaldist, mis hinnatakse funktsiooni kutsumisel. Lambda-avaldises ei saa kasutada tüübi märkusi. Tänapäeval kasutatakse Pythonis lambda-funktsioonide enamikus kasutamisjuhtudes teisi tehnikaid, näiteks arusaamu.
Lambda-funktsioonide erinevad kasutusviisid Pythonis
Lambdad on tuletatud funktsionaalsest programmeerimisest. Mõnes keeles, näiteks JavaScriptis, kasutatakse anonüümseid funktsioone laialdaselt ilma spetsiaalse märksõna vajaduseta. Pythonis kasutatakse lambda-väljendeid väikeste funktsioonide loomiseks kohalikult. Allpool vaatame läbi nende kõige kasulikumad rakendused.
Kuidas täita Pythoni kõrgemate järjekordade funktsioone lambdadega
Lambdasid kasutatakse sageli kõrgemate järjekordade funktsioonidega, nagu map(), filter() ja reduce(). Lambdade abil saab iterable’i elemente teisendada ilma tsükleid kasutamata. Kõrgemate järjekordade funktsioonid on funktsioonid, mis võtavad parameetritena funktsioone või tagastavad funktsiooni.
Funktsioon map() võtab parameetritena vastu funktsiooni ja iterable’i. See käivitab funktsiooni iga iterable’i elemendi jaoks. Proovime genereerida ruutnumbreid. Kasutame funktsiooni map() ja edastame argumendina lambda-väljendi, mis genereerib ruutfunktsiooni. Ruutfunktsiooni rakendatakse 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]pythonAlates Python 3.0 versioonist tagastavad funktsioonid map() ja filter() loendi asemel iterable’i. list() kutsutakse assert avaldistes, et iterable’id loendisse lahti pakkida.
Loendite mõistmine pakub kaasaegsemat lähenemisviisi iterable’ide töötlemiseks. Selle asemel, et kasutada map() ja luua lambda-funktsioon, saame kirjeldada operatsiooni 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]pythonFunktsiooni filter() abil saab filtreerida iterable’i elemente. Me võime laiendada oma näidet, et genereerida ainult paarisarvulisi ruutnumbreid:
# 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]pythonNäitame eelistatud lähenemisviisi, mille puhul kasutatakse loendi mõistmist, et saada sama tulemus ilma lambdade ja kõrgemate järku funktsioonide kasutamiseta. Mõistmise osa if kasutatakse, et filtreerida genereeritud ruutnumbrite hulgast välja paarisnumbrid:
# 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]pythonPythoni funktsioon reduce() ei ole alates Python 3.0 versioonist standardteegis sisaldunud. See funktsioon on leitav moodulis functools.
Kuidas rakendada Pythonis lambdade abil põhilisi funktsioone
Comprehensions on Pythonis suures osas asendanud klassikalised kõrgemate järjekordade funktsioonid map() ja filter(). Siiski saab lambdade kõiki eeliseid demonstreerida võtmefunktsioonide abil.
Pythoni võrdlusfunktsioonid sorted(), min() ja max() töötavad iterable’itega. Iga iterable’i element võrreldakse, kui funktsioon kutsutakse. Need kolm funktsiooni võtavad võtmefunktsiooni valikulise parameetrina key. Võtmefunktsioon kutsutakse iga elemendi jaoks ja see tagastab võrdlusoperatsiooni jaoks võtmeväärtuse.
Vaatleme järgmist probleemi. Meil on kaust pildifailidega, mille nimed on seotud Python-nimekirjaga. Soovime nimekirja sorteerida. Kõik failinimed algavad numbriga img, millele järgneb number:
# List of image file names
images = ['img1', 'img2', 'img30', 'img3', 'img22', 'img100']pythonKui kasutame Pythoni funktsiooni sorted(), kasutatakse leksikograafilist järjestust. See käsitleb järjestikuseid numbreid ühe numbrina. Seega paigutatakse numbrid ['1', '2', '100'] järjestusse ['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']pythonMe edastame lambda väljendi, mis loob võtmefunktsiooni, et tagada sorteerimise õigsus. Võtmefunktsioon eraldab failinime numbrilise 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']pythonVõtmefunktsiooni kasutatakse kohalikult ja ainult üks kord. Selle jaoks pole vaja määratleda eraldi nimega funktsiooni. Lambdad on õige viis võtmefunktsioonide loomiseks. Vaadakem veel kahte näidet.
Nagu sorted(), võtavad sisseehitatud Python-funktsioonid min() ja max() vastu valikulise võtmefunktsiooni. Funktsioonid leiavad nimekirjast või muust iteratiivsetest elementidest väikseima ja suurima elemendi. Väikseim või suurim element on määratluse küsimus ja seda saab täpsustada võtmefunktsiooni 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 spetsiaalset võtmefunktsiooni:
nums = [42, 69, 51, 13]
assert min(nums) == 13
assert max(nums) == 69pythonKui võtmefunktsiooni ei edastata, kasutatakse vaikimisi identiteedifunktsiooni f(x) = x. Seda saab hõlpsasti defineerida Python lambda abil lambda x: x.
Aga mis siis, kui iterable’i iga element sisaldab mitut kuupäeva? Kujutlegem nimekirja, mis sisaldab inimeste nimesid ja vanuseid. Millised on kriteeriumid min() ja max() puhul, kui otsustatakse, milline on väikseim ja suurim element? Siin on kasulik võtmefunktsioon.
Vajame näiteandmeid, et illustreerida võtmefunktsioonide tööpõhimõtet. Loome funktsiooni Person(), mis toimib konstruktorina:
# 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}pythonMe loome inimeste nimekirja, kasutades oma konstruktorifunktsiooni:
# Create list of people
people = [person('Jim', 42), person('Jack', 51), person('John', 69)]pythonLeiame vanima isiku, kasutades max() kõnet. See genereerib võtmefunktsiooni, kasutades lambda-väljendit, mis võtab isiku sõnastiku ja ekstraktib sellest vanuse võrdluselemendina:
# Find the oldest person
oldest = max(people, key=lambda person: person['age'])
# Check that it works
assert oldest == Person('John', 69)pythonSama lähenemisviis toimib ka funktsiooni min() puhul. Sel juhul määratleme võtmefunktsiooni väljaspool min() kutsumist ja kasutame taas lambda-väljendit. See parandab loetavust ja on kasulik, kui võtmefunktsioonil 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)pythonKuidas luua sulgureid Python lambdas
Python lambdas kasutatakse ka sulgude määratlemisel. Need on funktsioonid, mis on loodud teiste funktsioonide poolt ja salvestavad väärtuse. Sulgude abil saab luua sarnaste funktsioonide perekondi. Näitame tavalist näidet, kus luuakse võimsusfunktsioonid.
Võimsusfunktsioonid võtavad argumendi ja tõstavad selle eksponendiks. Ruutfunktsioon f(x) = x ^ 2 ja kuupfunktsioon f(x) = x ^ 3 on tuntud näited. Konstruktorfunktsiooni abil saab luua suvalisi võimsusfunktsioone kui sulgumisi. Kasutame lambda-väljendit, mis tähendab, et meil ei ole vaja määratleda sisemist nimelist funktsiooni:
# 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) == 1000pythonKuidas kasutada Python lambdas kohe käivitatavat funktsiooni väljendit (IIFE)
IIFE, hääldatakse „iffy”, on JavaScriptis tuntud muster. See hõlmab anonüümse funktsiooni määratlemist ja selle kohest täitmist.
Lambdasid saab kasutada IIFE-dena, kuigi need ei ole Pythoni piirangute tõttu eriti kasulikud. Me peame ainult lambda-väljendi ümber sulgudesse panema:
(lambda num: num * num)pythonJa veel üks sulgude paar, mis sisaldab argumenti (argumente):
assert (lambda num: num * num)(3) == 9python