Le comprehension sono uno strumento che ci permette di creare in modo conciso e conveniente nuove liste, set, e dizionari partendo da una sequenza di valori esistenti. Le comprehension ci permettono anche di filtrare e trasformare gli elementi.
Sintassi e esempi
Vediamo alcuni semplici esempi:
>>> # list comprehension che crea una lista di quadrati
>>> [x**2 for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>>
>>> # set comprehension che crea un set di cubi
>>> {x**3 for x in range(10)}
{0, 1, 64, 512, 8, 343, 216, 729, 27, 125}
>>>
>>> # dict comprehension che mappa lettere lowercase all'equivalente uppercase
>>> {c: c.upper() for c in 'abcde'}
{'c': 'C', 'e': 'E', 'a': 'A', 'b': 'B', 'd': 'D'}
Da questi esempi possiamo vedere che la sintassi è:
[expr for elem in seq]
per le list comprehension (listcomp){expr for elem in seq}
per le set comprehension (setcomp){expr: expr for elem in seq}
per le dict comprehension (dictcomp)
Per ogni elemento della sequenza (es. range(10)
o 'abcde'
), l'espressione viene valutata e il risultato viene aggiunto alla list/set/dict. Quanto tutti gli elementi sono stati creati, una nuova lista, set, o dizionario viene restituito.
Le comprehension ci permettono di creare questi nuovi oggetti senza dovere creare manualmente un oggetto vuoto e senza dover aggiungere gli elementi individualmente e sono quindi considerate syntactic sugar (zucchero sintattico). Ad esempio, la list comprehension seguente:
>>> squares = [x**2 for x in range(10)]
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
è equivalente al seguente blocco di codice:
>>> squares = []
>>> for x in range(10):
... squares.append(x**2)
...
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Possiamo notare alcune differenze:
- la list comprehension ottiene lo stesso risultato in 1 riga invece che 3 (o più);
- nella listcomp l'elemento (
x**2
) si trova all'inizio invece che alla fine; - nella listcomp non c'è bisogno di creare la lista vuota;
- nella listcomp non c'è bisogno di usare
list.append()
(oset.add()
odict[key] = value
) per aggiungere gli elementi alla lista/set/dict.
Le comprehension ci permettono anche di aggiungere for
addizionali e un if
per filtrare elementi:
>>> # listcomp che filtra i numeri e prende solo quelli pari
>>> [x for x in range(10) if x%2 == 0]
[0, 2, 4, 6, 8]
>>>
>>> # setcomp che filtra le lettere e prende solo quelle uppercase
>>> {c for c in 'aAbBcCdDeE' if c.isupper()}
{'C', 'E', 'B', 'A', 'D'}
>>>
>>> # listcomp che crea tutte le combinazioni tra ABC e 123
>>> [c+n for c in 'ABC' for n in '123']
['A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3']
La list comprehension:
>>> even = [x for x in range(10) if x%2 == 0]
>>> even
[0, 2, 4, 6, 8]
è equivalente al seguente blocco di codice:
>>> even = []
>>> for x in range(10):
... if x%2 == 0:
... even.append(x)
...
>>> even
[0, 2, 4, 6, 8]
mentre la list comprehension:
>>> combs = [c+n for c in 'ABC' for n in '123']
>>> combs
['A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3']
è equivalente al seguente blocco di codice:
>>> combs = []
>>> for c in 'ABC':
... for n in '123':
... combs.append(c+n)
...
>>> combs
['A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3']
Possiamo notare come in tutti gli esempi, l'ordine dei for
e if
nella comprehension corrisponde all'ordine della forma estesa.
map e filter
In Python ci sono 2 funzioni bultin che sono in grado di svolgere un ruolo simile alle comprehension:
map(func, seq)
: applica la funzionefunc
a tutti gli elementi diseq
e ritorna un nuovo iterabile;filter(func, seq)
: ritorna un iterabile che contiene tutti gli elementi diseq
per cuifunc(elem)
è true.
map(func, seq)
è simile a [func(elem) for elem in seq]
:
>>> # definisco una funzione che ritorna il quadrato di un numero
>>> def square(n):
... return n**2
...
>>> squares = map(square, range(10))
>>> squares # map ritorna un oggetto iterabile
<map object at 0xb6730d8c>
>>> list(squares) # convertendolo in lista si possono vedere gli elementi
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> # la seguente listcomp è equivalente a usare list(map(func, seq))
>>> [square(x) for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
filter(func, seq)
è simile a [elem for elem in seq if func(elem)]
:
>>> # definisco una funzione che ritorna True se il numero è pari
>>> def is_even(n):
... if n%2 == 0:
... return True
... else:
... return False
...
>>> even = filter(is_even, range(10))
>>> even # filter ritorna un oggetto iterabile
<filter object at 0xb717b92c>
>>> list(even) # convertendolo in lista si possono vedere gli elementi
[0, 2, 4, 6, 8]
>>> # la seguente listcomp è equivalente a usare list(filter(func, seq))
>>> [x for x in range(10) if is_even(x)]
[0, 2, 4, 6, 8]
Nonostante queste funzioni siano convenienti in alcuni casi, non hanno la stessa flessibilità delle comprehension.
In questi ultimi due esempi abbiamo definito due semplici funzioni. Nella prossima lezione impararemo meglio come usare il costrutto def
per definire funzioni.