Lambda-funktiot ovat olleet funk­tio­naa­li­sen oh­jel­moin­nin menetelmä Python 1.0 :sta lähtien. Viime vuosina muut tekniikat ovat kuitenkin saa­vut­ta­neet suuremman suosion ja kor­van­neet lambdat suurelta osin. Siitä huo­li­mat­ta lamb­doil­la on edelleen joitakin eri­kois­tu­nei­ta käyt­tö­tar­koi­tuk­sia, jotka taitavien Python-oh­jel­moi­jien tulisi tuntea.

Mitä ovat lambda-funktiot Pyt­ho­nis­sa?

Pyt­ho­nis­sa lambda-funktio viittaa ni­met­tö­mään funktioon. Python käyttää avain­sa­naa lambda lambda-funktion luomiseen. Lambda-lauseke koostuu avain­sa­nas­ta lambda, jota seuraa ar­gu­ment­tien luettelo, kak­sois­pis­te ja yk­sit­täi­nen lauseke. Lauseke annetaan ar­gu­men­teil­la ja ar­vioi­daan, kun lambda-funktio kutsutaan:

lambda argument: expression

Funktiot ovat lähes jokaisen oh­jel­moin­ti­kie­len pe­rus­ra­ken­tei­ta, ja ne edustavat pienintä uu­del­leen­käy­tet­tä­vää koo­diyk­sik­köä. Tyy­pil­li­ses­ti Pythonin funktiot mää­ri­tel­lään def. Esitämme esi­merk­ki­nä ne­liö­funk­tion, joka kertoo luvun itsellään:

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

def on tunnettu tapa mää­ri­tel­lä funk­tioi­ta Pyt­ho­nis­sa, mutta kielessä on myös lambda-funktiot. Nämä ovat ni­met­tö­miä funk­tioi­ta, jotka mää­rit­te­le­vät lausek­keen pa­ra­met­reil­la. Lambda-funk­tioi­ta voidaan käyttää missä tahansa, missä funktiota odotetaan tai voidaan määrittää nimelle. Voit nähdä ne­liö­funk­tio­ta vastaavan lambda-lausek­keen tässä:

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

Mitä eroa on lambda- ja def-sanoilla?

Saattaa tuntua oudolta, että Pyt­ho­nis­sa voi luoda funk­tioi­ta sekä lambda että def. Lambda ei kui­ten­kaan ole oma omi­nai­suus, vaan vain toinen tapa luoda lyhyitä funk­tioi­ta pai­kal­li­ses­ti. Jokainen lambda lla luotu funktio voidaan luoda myös def. Päin­vas­toin tämä ei kui­ten­kaan päde.

Syn­tak­ti­sel­la tasolla lambda ja def ovat molemmat avain­sa­no­ja. Yksi keskeinen ero niiden välillä on Pythonin tiukka lausek­kei­den ja lausumien erottelu. Lausek­keet ovat koodin suo­rit­ta­mi­sen vaiheita, kun taas lausumat ar­vioi­daan arvoksi.

Def aloittaa lauseen, tarkemmin sanottuna yh­dis­te­tyn lauseen, joka sisältää muita lauseita. def voi esiintyä vain return lausetta. return palauttaa arvon, kun def llä mää­ri­tel­ty funktio kutsutaan.

Toisin kuin def, lambda aloittaa lausek­keen, joka ei voi sisältää lauseita. Lambda-lauseke ottaa yhden tai useamman ar­gu­men­tin ja palauttaa ni­met­tö­män funktion. Kun lambda-funktio kutsutaan, sen sisältämä lauseke ar­vioi­daan an­ne­tuil­la ar­gu­men­teil­la ja pa­lau­te­taan.

Mitkä ovat Pythonin lambda-lausek­kei­den ra­joi­tuk­set?

Python on tar­koi­tuk­sel­la ra­joit­ta­nut lambda-funk­tioi­den käyt­tö­kel­poi­suut­ta, koska yleensä on parempi nimetä funktiot. Tämä pakottaa oh­jel­moi­jat miet­ti­mään funktion mer­ki­tys­tä ja erot­ta­maan osat selkeästi toi­sis­taan.

Lambdat eivät voi sisältää ohjeita, toisin kuin def mää­ri­tel­lyn funktion runko. Siksi lambda-funk­tios­sa ei ole mah­dol­lis­ta käyttää if, for jne. Ei ole myöskään mah­dol­lis­ta laukaista poik­keus­ta, koska se edel­lyt­tää raise.

Pythonin lambda-funktiot voivat sisältää yhden lausek­keen, joka ar­vioi­daan kut­sut­taes­sa. Tyyp­pi­mer­kin­tö­jä ei voi käyttää lambda-lausek­kees­sa. Nykyään useim­mis­sa Pythonin lambda-funk­tioi­den käyt­tö­ta­pauk­sis­sa käytetään muita tek­nii­koi­ta, kuten compre­hen­sions.

Lambda-funk­tioi­den eri käyt­tö­tar­koi­tuk­set Pyt­ho­nis­sa

Lambdat ovat peräisin funk­tio­naa­li­ses­ta oh­jel­moin­nis­ta. Joissakin kielissä, kuten Ja­vaSc­rip­tis­sä, ni­met­tö­miä funk­tioi­ta käytetään laajalti ilman erityistä avain­sa­naa. Pyt­ho­nis­sa lambda-lausek­kei­ta käytetään pienten funk­tioi­den luomiseen pai­kal­li­ses­ti. Seu­raa­vas­sa käymme läpi niiden hyö­dyl­li­sim­mät so­vel­luk­set.

Kuinka täyttää kor­keam­man asteen funktiot Pyt­ho­nis­sa lamb­doil­la

Lambdoja käytetään usein kor­keam­man asteen funk­tiois­sa, kuten map(), filter() ja reduce(). Ite­roi­ta­van elementit voidaan muuntaa ilman sil­mu­koi­ta lambdojen ansiosta. Kor­keam­man asteen funktiot ovat funk­tioi­ta, jotka ottavat pa­ra­met­reik­si funk­tioi­ta tai pa­laut­ta­vat funktion.

map() ottaa pa­ra­met­rei­na funktion ja ite­rat­ta­van. Se suorittaa funktion jo­kai­sel­le ite­rat­ta­van ele­men­til­le. Ko­keil­laan­pa generoida ne­liö­lu­ku­ja. Käytämme map() ja välitämme ar­gu­ment­ti­na lambda-lausek­keen, joka generoi ne­liö­funk­tion. Ne­liö­funk­tio so­vel­le­taan jo­kai­sel­le listan ele­men­til­le map()lla:

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
Huomio

Python 3.0:sta alkaen funktiot map() ja filter() pa­laut­ta­vat iterable-tyyppisen arvon listan sijaan. list() kutsu käytetään assert -lauseiden sisällä iterable-tyyp­pis­ten arvojen pur­ka­mi­seen listaksi.

List compre­hen­sion tarjoaa mo­der­nim­man lä­hes­ty­mis­ta­van iterable-tyyp­pis­ten tietojen kä­sit­te­lyyn. Sen sijaan, että tur­vau­tui­sim­me map() een ja luo­mi­sim­me lambda-funktion, voimme kuvata ope­raa­tion suoraan:

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

filter() voidaan käyttää suo­dat­ta­maan ite­rat­ta­van ele­ment­te­jä. Voimme laajentaa esi­merk­kiäm­me tuot­ta­maan vain pa­ril­li­sia ne­liö­lu­ku­ja:

# 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

Esitämme suo­si­tel­lun lä­hes­ty­mis­ta­van, jossa käytetään listan ym­mär­tä­mis­tä saman tuloksen tuot­ta­mi­seen ilman lambda-funk­tioi­ta ja kor­keam­man asteen funk­tioi­ta. Ym­mär­tä­mi­sen osaa if käytetään suo­dat­ta­maan pa­ril­li­set luvut tuo­te­tuis­ta ne­liö­lu­vuis­ta:

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

Pythonin reduce() -funktiota ei ole si­säl­ly­tet­ty va­kio­kir­jas­toon Python 3.0:n jälkeen. Tämä funktio löytyy functools -mo­duu­lis­ta.

Kuinka toteuttaa keskeisiä toi­min­to­ja Pyt­ho­nis­sa lamb­doil­la

Compre­hen­sions ovat suurelta osin kor­van­neet klassiset kor­keam­man asteen funktiot map() ja filter() Pyt­ho­nis­sa. Kuitenkin avain­funk­tioi­ta voidaan käyttää lambda-funk­tioi­den kaikkien vah­vuuk­sien esit­te­le­mi­seen.

Pythonin ver­tai­lu­funk­tiot sorted(), min() ja max() toimivat iterable-tyyp­pi­sil­lä kohteilla. Jokainen iterable-tyyppisen kohteen elementti verrataan, kun funktio kutsutaan. Nämä kolme funktiota ottavat vastaan avain­funk­tion va­lin­nai­se­na pa­ra­met­ri­na key. Avain­funk­tio kutsutaan jo­kai­sel­le ele­men­til­le ja palauttaa avai­nar­von vertailua varten.

Tar­kas­tel­laan seuraavaa ongelmaa. Meillä on kansio, jossa on ku­va­tie­dos­to­ja, joiden nimet on liitetty Python-listaan. Haluamme lajitella luettelon. Kaikki tie­dos­to­ni­met alkavat numerolla img, jota seuraa numero:

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

Jos käytämme Pythonin sorted(), käytetään lek­si­kogra­fis­ta jär­jes­tys­tä. Tämä kä­sit­te­lee pe­räk­käi­siä numeroita yk­sit­täi­si­nä numeroina. Näin ollen numerot ['1', '2', '100'] si­joi­te­taan jär­jes­tyk­seen ['1', '100', '2']. Tulos ei ole odotetun mukainen:

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

Syötämme lambda, joka tuottaa avain­funk­tion, jolla var­mis­te­taan, että lajittelu on oikea. Avain­funk­tio poimii tie­dos­to­ni­men nu­mee­ri­sen osan, jota sorted() käyttää avaimena:

# 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

Avain­funk­tio­ta käytetään pai­kal­li­ses­ti ja vain kerran. Sille ei tarvitse mää­ri­tel­lä erillistä nimettyä funktiota. Lambdat ovat oikea tapa luoda avain­funk­tioi­ta. Kat­so­taan­pa vielä kaksi esi­merk­kiä.

Kuten sorted(), myös si­sään­ra­ken­ne­tut Python-funktiot min() ja max() ottavat vastaan va­lin­nai­sen avain­funk­tion. Funktiot etsivät pienimmän ja suurimman elementin listasta tai muusta ite­rat­ta­vas­ta. Pienin tai suurin elementti on mää­ri­tel­mä­ky­sy­mys, ja se voidaan määrittää avain­funk­tion avulla.

Yk­sin­ker­tais­ten arvojen luet­te­lois­sa, kuten nu­me­ro­luet­te­los­sa, on selvää, mitä pie­nim­mäl­lä tai suu­rim­mal­la ele­men­til­lä tar­koi­te­taan. Tässä ta­pauk­ses­sa emme tarvitse erityistä avain­funk­tio­ta:

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

Jos avain­funk­tio­ta ei välitetä, käytetään ole­tusar­voi­ses­ti iden­ti­teet­ti­funk­tio­ta f(x) = x. Tämä voidaan helposti mää­ri­tel­lä Python-lambda-funktiona lambda x: x.

Mutta entä jos jokainen ite­rat­ta­van elementti sisältää useita päi­vä­mää­riä? Ku­vi­tel­laan lista sa­na­kir­jois­ta, jotka edustavat ihmisiä heidän nimillään ja ikänsä. Mikä on kriteeri min() lle ja max(), kun päätetään, mikä on pienin ja suurin elementti? Tässä ti­lan­tees­sa avain­funk­tio on hyö­dyl­li­nen.

Tar­vit­sem­me esi­merk­ki­tie­to­ja ha­vain­nol­lis­taak­sem­me avain­toi­min­to­jen toimintaa. Luodaan funktio Person(), joka toimii kon­struk­to­ri­na:

# 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

Luomme luettelon ihmisistä käyt­tä­mäl­lä kon­struk­to­ri­funk­tio­tam­me:

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

Etsimme vanhimman henkilön käyt­tä­mäl­lä max(). Tämä luo avain­funk­tion käyt­tä­mäl­lä lambda-lause­ket­ta, joka ottaa henkilön sa­na­kir­jan ja poimii siitä iän ver­tai­lue­le­men­tik­si:

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

Lä­hes­ty­mis­ta­pa toimii täs­mäl­leen samalla tavalla min() funk­tios­sa. Tässä ta­pauk­ses­sa mää­ri­tel­lään avain­funk­tio min() kutsun ul­ko­puo­lel­la ja käytetään jälleen lambda-lause­ket­ta. Tämä parantaa luet­ta­vuut­ta ja on hyö­dyl­lis­tä, jos avain­funk­tiol­la on useita pai­kal­li­sia käyt­tö­tar­koi­tuk­sia:

# 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

Kuinka luoda sulkeumia Python-lamb­doil­la

Python-lambdoja käytetään myös sul­keu­mien mää­rit­te­lys­sä. Nämä ovat funk­tioi­ta, jotka luodaan muiden funk­tioi­den avulla ja jotka tal­len­ta­vat arvon. Sulkeumia voidaan käyttää luomaan sa­man­kal­tais­ten funk­tioi­den ryhmiä. Esitämme yleisen esimerkin, jossa luodaan po­tens­si­funk­tioi­ta.

Po­tens­si­funk­tiot ottavat ar­gu­men­tin ja ko­rot­ta­vat sen po­tens­siin. Ne­liö­funk­tio f(x) = x ^ 2 ja kuu­tio­funk­tio f(x) = x ^ 3 ovat tun­net­tu­ja esi­merk­ke­jä. Mie­li­val­tai­sia po­tens­si­funk­tioi­ta voidaan luoda sul­keu­mi­na käyt­tä­mäl­lä kon­struk­to­ri­funk­tio­ta. Käytämme lambda-lause­ket­ta, mikä tar­koit­taa, että meidän ei tarvitse mää­ri­tel­lä sisäistä nimettyä funktiota:

# 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

Kuinka käyttää vä­lit­tö­mäs­ti kutsuttua funk­tio­lause­ket­ta (IIFE) Python-lamb­doil­la

IIFE, lausutaan “iffy”, on tunnettu malli Ja­vaSc­rip­tis­sä. Siinä mää­ri­tel­lään nimetön funktio ja suo­ri­te­taan se vä­lit­tö­mäs­ti.

Lambdat voidaan käyttää IIFE-lausek­kei­na, vaikka ne eivät ole kovin hyö­dyl­li­siä Pythonin ra­joi­tus­ten vuoksi. Meidän tarvitsee vain laittaa sulkeet lambda-lausek­keen ympärille:

(lambda num: num * num)
python

Ja toinen suluissa oleva ar­gu­ment­ti (ar­gu­men­tit):

assert (lambda num: num * num)(3) == 9
python
Siirry pää­va­lik­koon