Funkce definované uvnitř jiných funkcí mají jednu podivuhodnou vlastnost – jsou-li z nadřazené funkce vráceny, nesou s sebou kontext platný v době jejich vrácení. Nejlépe osvětlí příklad:
# toto je vnější funkce, „generující“ uzávěr
def přičti(kolik):
# tato vnitřní funkce vidí na proměnné nadřazené funkce,
# tj. i na argument 'kolik'
def vnitřní_fce(k_čemu):
return kolik + k_čemu
# vnitřní funkci vrátíme „upravenou“ o argument 'kolik'
# z doby jejího vrácení
return vnitřní_fce
# „Vyrobme“ funkci, která bude ke svému argumentu přičítat 5:
>>> přičti_5 = přičti(5)
# Je to opravdu funkce, a to ta očekávaná (vnitřní):
>>> přičti_5
<function vnitřní_fce at 0x00E2D108>
# Vyzkoušejme to:
>>> přičti_5(3)
8
# Na věci nic nezmění, když vyrobíme další funkci s jiným číslem:
>>> přičti_3 = přičti(3)
>>> přičti_3
<function vnitřní_fce at 0x00E2D0C0>
>>> přičti_3(3)
6
>>> přičti_5(10)
15
Jelikož každá z vnitřních funkcí si sebou nese kontext platný v okamžiku jejich vrácení, tedy i hodnoty proměnných (a tudíž i argumentů) v nadřazené funkci v tu chvíli, odpovídají přičítací funkce z předchozího slajdu vlastně následujícím definicím:
Takovéto funkce jsou v Python'u velmi často používány (nejkřiklavější příklad nabízí asi dekorátory) a říká se jim, případně i takovémuto chování obecně, právě uzávěry (closures).
Jelikož volání funkce přičti() vrací funkci, můžete samozřejmě napsat třeba i tohle:
>>> přičti(5)(3)
8
Ne že by to tedy bylo dvakrát čitelné, zvlášť kdybyste uzávěrů za sebou zřetězili vícero…
Ačkoli předchozí (hodně umělý) příklad snad osvětlil, jak se uzávěr chová, pro vyškolené paradigmatem OOP vymyslel před lety Steve Majewski krásný příměr:
Objekt je balík dat, ke kterému jsou přidané metody.
Uzávěr je procedura, ke které jsou přidána data.
Jejich poměrná neznalost je asi v přímé úměře k rozšíření OOP – jejich hájemstvím jsou totiž převážně funkcionální programovací jazyky a těžko si představit něco víc ortogonálního k OOP než právě je.
Lambda-funkce v Python'u jsou anonymní (tj. bezejmenné) funkce definované typicky v rámci jednoho řádku a skládající se pouze z jednoho výrazu, který je vyhodnocen a automaticky vrácen ve chvíli volání funkce:
lambda [ARGUMENT[Y]]: VÝRAZ
Pythonovské lambda-funkce tedy nemají jméno, jejich argumenty nejsou uzavřeny v závorkách a spočítaná hodnota (či objekt) jejich těla je automaticky vrácena, aniž by bylo třeba použít příkaz return.
Přesněji řečeno je tato definice platná pouze pro Python, protože v něm jsou lambda-funkce oproti běžně chápanému pojetí tohoto termínu velmi „osekány“. Typicky totiž těla lambda-funkcí nejsou omezena pouze na jeden jediný výraz – ve většině jazyků, které je obsahují, jsou lambda-funkce plnohodnotné funkce, které prostě jen nemají vlastní záznam v tabulce jmen programu a vyskytují se na místě, kde by jinak stál odkaz na „běžnou“ funkci.
def přičti(kolik):
return lambda k_čemu: kolik + k_čemu
>>> přičti_5 = přičti(5)
>>> přičti_5
<function <lambda> at 0x00DC9CD8>
>>> přičti_5(3)
8
Asi to není na první pohled tak pěkný zápis, ale má dvě výhody:
Bez dalšího zkoumání každý vidí, že návratovým typem funkce přičti() je funkce.
Do tabulky lokálních objektů funkce přičti() není potřeba zavádět další funkci (zde vnitřní_fce()), čímž se ušetří dost prostředků z „režije“ programu.
Lambda-funkce nachází použití všude tam, kde je požadována funkce, ale přitom není potřeba kvůli tomu zavádět pojmenovanou funkci na obecnější úrovni.
Vzhledem k „osekanosti“ lambda-funkcí v Python'u se s nimi tedy většinou setkáme na místech parametrů funkcí, kde je uvedené omezení na jednu řádku a jeden výraz nakonec ještě i celkem vítáno. Několik klasických případů ilustrují následující příklady.
sorted( xs, key = lambda x: x[2] ) #
sorted( xs, key = lambda x: len(x) ) # sorted( xs, key=len )
Pro srovnání – první z příkladů by se pomocí pojmenované funkce zapsal takto:
def fn(x):
return x[2]
sorted(xs, key=fn)
def mymap(xs, fn=lambda x: x):
return [fn(x) for x in xs]
# Výchozí chování pojmenovaného argumentu 'fn' je „identita“:
>>> mymap( ['a', 'b', 'c', ] )
['a', 'b', 'c']
# „Přepišme“ identitu vestavěnou funkcí 'ord':
>>> mymap( ['a', 'b', 'c', ], ord )
[97, 98, 99]
# Podstrčme funkci nějakou naši vlastní „šílenost“:
>>> mymap( ['a', 'b', 'c', ], lambda x: chr(ord(x)+3) )
['d', 'e', 'f']
Funkci můžete samozřejmě volat i jako mymap('abc', ord) a podobně.
>>> from collections import defaultdict
>>> xs = "abrakadabra"
>>> xd = defaultdict(
lambda: ['samohláska', 0] if x in 'aeiouy' else ['souhláska', 0]
)
>>> for x in xs:
... xd[x][1] += 1
...
>>> xd
defaultdict(<function <lambda> at 0x7fc7473b96a8>,
{'b': ['souhláska', 2],
'd': ['souhláska', 1],
'r': ['souhláska', 2],
'a': ['samohláska', 5],
'k': ['souhláska', 1]}
)
(Pozalamováno pro lepší čitelnost.)