Le funzioni lambda esistono sin da Python 1.0 come mezzo per la pro­gram­ma­zio­ne fun­zio­na­le. Oggi il loro utilizzo è stato so­sti­tui­to in larga parte da altre tecniche. Tuttavia vi sono ancora alcuni ambiti spe­cia­liz­za­ti in cui i pro­gram­ma­to­ri Python più esperti do­vreb­be­ro saperle uti­liz­za­re.

Che cosa sono le funzioni lambda in Python?

Con il termine “funzione lambda” in Python si intende una funzione anonima. Per creare una funzione lambda in Python si utilizza la parola chiave lambda. Un’espres­sio­ne lambda è composta dalla parola chiave lambda seguita da un elenco di argomenti, dai due punti e da una singola espres­sio­ne (“ex­pres­sion”). Ri­chia­man­do la funzione lambda, l’espres­sio­ne viene dotata degli argomenti e valutata:

lambda argument: expression

Le funzioni sono un costrutto lin­gui­sti­co fon­da­men­ta­le di quasi tutti i linguaggi di pro­gram­ma­zio­ne e co­sti­tui­sco­no la minima unità di codice riu­ti­liz­za­bi­le. Di solito le funzioni in Python sono definite con la parola chiave def. Come esempio, vi mostriamo la funzione di Python che eleva un numero al quadrato mol­ti­pli­can­do­lo per sé stesso:

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

Oltre al noto metodo usato per definire le funzioni in Python con la parola chiave def, il lin­guag­gio di pro­gram­ma­zio­ne dispone anche delle “lambda”. Si tratta di brevi funzioni anonime (ossia a cui non è stato assegnato un nome) che de­fi­ni­sco­no un’espres­sio­ne con dei parametri. Le lambda si uti­liz­za­no in tutti i casi in cui è prevista una funzione oppure si collegano a un nome tramite as­se­gna­zio­ne. Questa è l’espres­sio­ne lambda equi­va­len­te alla funzione per elevare al quadrato:

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

Il termine “funzione lambda” indica in Python una funzione creata con la parola chiave lambda. Lambda non è il nome di una funzione e non si tratta di un operatore Python.

Qual è la dif­fe­ren­za fra lambda e def?

A prima vista può sembrare strano che in Python vi siano due modi per creare le funzioni, ossia con lambda e con def. In realtà, lambda non è un metodo vero e proprio, ma soltanto una diversa sintassi con cui scrivere brevi funzioni in locale. Infatti, ogni funzione creata con lambda può essere creata anche con def. Tuttavia, non è vero il contrario.

A livello sin­tat­ti­co, sia lambda che def sono parole chiave. Una dif­fe­ren­za fra le due fun­zio­na­li­tà sta nella rigorosa di­stin­zio­ne tra istru­zio­ne (“statement”) ed espres­sio­ne (“ex­pres­sion”) in Python. In poche parole, le istru­zio­ni sono fasi di ese­cu­zio­ne del codice, mentre le espres­sio­ni sono valutate per re­sti­tui­re un valore.

La parola chiave def indica l’inizio di un’istru­zio­ne (nello specifico, un’“istru­zio­ne composta”) che contiene ulteriori istru­zio­ni. All’interno dell’istru­zio­ne def (e solo qui) possono esservi delle istru­zio­ni return. Ri­chia­man­do la funzione definita con def, un’istru­zio­ne return re­sti­tui­sce un valore.

A dif­fe­ren­za dell’istru­zio­ne def, la parola chiave lambda indica l’inizio di un’espres­sio­ne che non può contenere istru­zio­ni. L’espres­sio­ne lambda riceve uno o più argomenti e re­sti­tui­sce una funzione anonima. Ri­chia­man­do la funzione generata con lambda, l’espres­sio­ne che contiene viene valutata con gli argomenti indicati e quindi re­sti­tui­ta.

Quali sono le li­mi­ta­zio­ni delle espres­sio­ni lambda in Python?

Python limita ap­po­si­ta­men­te l’utilizzo delle funzioni lambda, in quanto di norma è meglio assegnare un nome alle funzioni. Questa scelta obbliga i pro­gram­ma­to­ri a ri­flet­te­re sul si­gni­fi­ca­to della funzione e a separare i vari com­po­nen­ti in modo chiaro l’uno dall’altro.

A dif­fe­ren­za del corpo di una funzione definita con la parola chiave def, le lambda non possono contenere alcuna istru­zio­ne. Pertanto non è im­pos­si­bi­le uti­liz­za­re if, for ecc. all’interno di una funzione lambda. Ana­lo­ga­men­te, non è possibile sollevare un’eccezione, in quanto per farlo serve l’istru­zio­ne raise.

Le funzioni lambda in Python possono contenere soltanto una singola espres­sio­ne, che viene valutata quando viene ri­chia­ma­ta. All’interno dell’espres­sio­ne lambda non è possibile uti­liz­za­re an­no­ta­zio­ni di tipo. Oggi per la maggior parte delle ap­pli­ca­zio­ni delle funzioni lambda in Python si uti­liz­za­no altre tecniche. Al riguardo, vale la pena men­zio­na­re in par­ti­co­la­re le com­pren­sio­ni.

Per cosa si uti­liz­za­no le funzioni lambda in Python?

In generale, le lambda pro­ven­go­no dalla pro­gram­ma­zio­ne fun­zio­na­le. In alcuni linguaggi, come Ja­va­Script, si uti­liz­za­no spesso le funzioni anonime senza ricorrere a una par­ti­co­la­re parola chiave. In Python, le espres­sio­ni lambda servono a creare piccole funzioni in locale senza troppi fronzoli. Nelle parti seguenti vi spie­ghia­mo le loro ap­pli­ca­zio­ni più utili.

Utilizzo di funzioni di ordine superiore in Python con le lambda

Spesso si uti­liz­za­no le lambda in con­nes­sio­ne con funzioni di ordine superiore, come map(), filter() e reduce(). Grazie alle lambda è possibile tra­sfor­ma­re gli elementi di un “iterabile” senza l’uso di cicli. Per funzioni di ordine superiore (in inglese, “higher-order functions”) si intendono funzioni che assumono altre funzioni come parametri oppure che re­sti­tui­sco­no una funzione.

La funzione map() assume come parametro una funzione e un iterabile ed esegue quindi la funzione per ciascun elemento dell’iterabile. Con­si­de­ria­mo il problema di generare numeri al quadrato: uti­liz­zia­mo la funzione map() e as­se­gnia­mo un’espres­sio­ne lambda come argomento che genera la funzione di ele­va­men­to al quadrato. Con map() si applica la funzione di ele­va­men­to al quadrato a ogni elemento della lista:

nums = [3, 5, 7]
# Square numbers using using `map()` and `lambda`
squares = map(lambda num: num ** 2, nums)
# Show that it works
assert list(squares) == [9, 25, 49]
python
N.B.

A partire da Python 3.0, le funzioni map() e filter() re­sti­tui­sco­no un iterabile anziché una lista. All’interno dell’istru­zio­ne assert uti­liz­zia­mo una chiamata list() per spac­chet­ta­re l’iterabile ottenendo una lista.

Le com­pren­sio­ni di lista offrono oggi un approccio moderno e pre­fe­ri­bi­le per l’ela­bo­ra­zio­ne di iterabili. Anziché ricorrere a map() e creare una funzione lambda, è possibile de­scri­ve­re l’ope­ra­zio­ne di­ret­ta­men­te:

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

Con la funzione filter() è possibile filtrare gli elementi di un iterabile. Al­lar­ghia­mo il nostro esempio in modo da generare solo numeri al quadrato pari:

# 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

Qui mostriamo nuo­va­men­te l’approccio moderno e pre­fe­ri­bi­le per generare lo stesso risultato senza uso di lambda e funzioni di ordine superiore per mezzo delle com­pren­sio­ni di lista. A tal fine usiamo il com­po­nen­te if della com­pren­sio­ne per filtrare i numeri al quadrato pari:

# 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
N.B.

La funzione reduce() di Python non fa più parte della libreria standard a partire da Python 3.0. Ora si trova nel modulo functools.

Rea­liz­za­zio­ne di funzioni chiave in Python sotto forma di lambda

Le com­pren­sio­ni hanno so­sti­tui­to in larga parte l’uso delle classiche funzioni di ordine superiore map() e filter() in Python. Con le co­sid­det­te “key functions”, in italiano “funzioni chiave”, esiste comunque uno scenario ap­pli­ca­ti­vo in cui le lambda mostrano tutto il loro po­ten­zia­le.

Le funzioni di confronto Python sorted(), min() e max() operano su iterabili. Alla chiamata, ciascun elemento dell’iterabile viene sot­to­po­sto a un confronto. Le tre funzioni assumono tutte una funzione chiave sotto forma di parametro key opzionale. La funzione chiave è chiamata per ciascun elemento e re­sti­tui­sce un valore chiave per l’ope­ra­zio­ne di confronto.

Come esempio, con­si­de­ria­mo il problema seguente. È presente una cartella con­te­nen­te file di immagini, i cui nomi sono riportati in una lista di Python. L’obiettivo è mettere in ordine la lista. I nomi dei file iniziano tutti per img, seguito da una nu­me­ra­zio­ne:

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

Se usiamo la funzione sorted() di Python, si utilizza il co­sid­det­to “ordine les­si­co­gra­fi­co”. In pratica, la funzione gestisce le cifre con­se­cu­ti­ve come singoli numeri. In tal modo, i numeri ['1', '2', '100'] vengono ordinati come ['1', '100', '2']. Il risultato non cor­ri­spon­de alle nostre aspet­ta­ti­ve:

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

Per ottenere l’ordine de­si­de­ra­to, creiamo un’espres­sio­ne lambda che genera una funzione chiave. La funzione chiave estrae la com­po­nen­te numerica di un nome del file, che viene quindi uti­liz­za­ta da sorted() come chiave:

# 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

La funzione chiave viene uti­liz­za­ta solo in locale e una volta sola. Non è ne­ces­sa­rio definire ap­po­si­ta­men­te un’ulteriore funzione dotata di nome. Le lambda sono quindi lo strumento giusto per creare funzioni chiave. Con­si­de­ria­mo altri due esempi.

Oltre a sorted(), le funzioni min() e max() integrate in Python assumono una funzione chiave opzionale. Le funzioni in­di­vi­dua­no l’elemento più piccolo e/o più grande in una lista o in un altro iterabile. Cosa sia esat­ta­men­te l’elemento più piccolo e/o più grande dipende dalla de­fi­ni­zio­ne ed è quindi de­fi­ni­bi­le per mezzo della funzione chiave.

Nelle liste che con­ten­go­no valori semplici, ad esempio una lista di numeri, è chiaro che cosa si intenda per “più piccolo” e “più grande”. In questo caso non serve alcuna funzione chiave speciale:

nums = [42, 69, 51, 13]
assert min(nums) == 13
assert max(nums) == 69
python
N.B.

Se non si assegna nessuna funzione chiave, si utilizza im­pli­ci­ta­men­te la funzione identità f(x) = x. Quest’ultima è fa­cil­men­te de­fi­ni­bi­le come funzione lambda di Python con lambda x: x.

Cosa succede invece se ciascuno degli elementi di un iterabile comprende più dati? Im­ma­gi­nia­mo di avere una lista di dict che rap­pre­sen­ta­no le persone con nome ed età. Quale criterio devono usare min() e max() per stabilire qual è l’elemento più piccolo e/o più grande? La funzione chiave serve proprio a questo.

Per capire meglio come fun­zio­na­no le funzioni chiave servono dei dati di esempio. Creiamo una funzione Person() che funge da co­strut­to­re:

# Constructor function for dict representing a person
def Person(name, age):
    return {'nome': name, 'età': age}
# Check that it works as expected
assert Person('Mario', 42) == {'nome': 'Mario', 'età': 42}
python

Con questa funzione co­strut­to­re creiamo una lista di persone:

# Create list of people
people = [Person('Mario', 42), Person('Roberto', 51), Person('Giovanni', 69)]
python

Suc­ces­si­va­men­te troviamo la persona più vecchia per mezzo della chiamata max(). A tal fine usiamo l’espres­sio­ne lambda per creare una funzione chiave che elabora un dict di una persona e ne estrae l’età come elemento per il confronto:

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

L’approccio funziona esat­ta­men­te allo stesso modo anche per la funzione min(). In questo caso definiamo la funzione chiave al di fuori della chiamata min() uti­liz­zan­do nuo­va­men­te un’espres­sio­ne lambda. Questa soluzione migliora la leg­gi­bi­li­tà ed è utile quando si utilizza la funzione chiave più volte in locale:

# 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('Mario', 42)
python

Creazione di “closure” con le lambda in Python

Un ulteriore utilizzo per le lambda in Python è la de­fi­ni­zio­ne delle co­sid­det­te “closure”. Si tratta di funzioni generate da altre funzioni e che me­mo­riz­za­no un valore. Le “closure” per­met­to­no ad esempio di creare famiglie di funzioni simili. Vi mostriamo l’esempio più comune per la creazione di funzioni di potenza.

Le funzioni di potenza assumono un argomento e lo elevano a potenza. Esempi noti sono la funzione di ele­va­men­to al quadrato f(x) = x ^ 2 e la funzione di ele­va­men­to al cubo f(x) = x ^ 3. Tramite una funzione co­strut­to­re è possibile creare funzioni di potenza a piacere sotto forma di “closure”. Uti­liz­zia­mo un’espres­sio­ne lambda evitando quindi di definire una funzione interna dotata di un nome:

# 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

Espres­sio­ni IIFE (Im­me­dia­te­ly Invoked Function Ex­pres­sion) con le lambda in Python

Le IIFE (pronuncia: “iffy”) sono un modello ben noto in Ja­va­Script. Con questo modello si definisce una funzione anonima, che viene eseguita im­me­dia­ta­men­te.

Anche se le li­mi­ta­zio­ni in Python le rendono poco utili, le lambda sono uti­liz­za­bi­li come IIFE. È suf­fi­cien­te mettere l’espres­sio­ne lambda fra parentesi:

(lambda num: num * num)
python

Un’altra coppia di parentesi deve contenere l’argomento o gli argomenti:

assert (lambda num: num * num)(3) == 9
python
Consiglio

Per muovere i primi passi, vi con­si­glia­mo di con­sul­ta­re il nostro tutorial su Python.

Vai al menu prin­ci­pa­le