Nella pratica della programmazione accade spesso di dover creare una lista usando un ciclo for
che calcola
i suoi elementi. Ecco un semplice esempio in IDLE che crea una lista di quadrati:
>>> listaquad = [] >>> for i in range(1, 11): listaquad.append(i ** 2) >>> listaquad
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
(per far eseguire ad IDLE il ciclo for
dovete premere due volte <INVIO>). Per casi come
questo Python ha una sintassi molto più compatta e leggibile, quella delle list comprehensions.
Infatti potete ottenere lo stesso risultato con una sola riga di codice, così:
>>> listaquad = [i ** 2 for i in range(1, 11)] >>> listaquad
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
L'espressione tra parentesi quadre nella prima istruzione non è una lista, ma una list comprehension, cioè un tipo di espressione che crea una lista "al volo"; vediamo la sua sintassi:
for
ed (opzionalmente) da uno o più if
.Quando incontra una comprehension Python crea automaticamente una lista (che
sarà il risultato della comprehension) e vi aggiunge tutti i valori
dell'espressione al suo interno calcolati eseguendo il ciclo for
. Quindi una
comprehension non fa nulla di nuovo, ma permette di semplificare notevolmente il codice, ottenendo con
una sintassi compatta ed intuitiva quello che richiederebbe parecchie istruzioni (i programmatori parlano di
"syntactic sugar": qualcosa di non strettamente necessario ma comunque esteticamente bello ed utile).
Vediamo ora qualche altro esempio:
>>> ["aaa" + c for c in "wxyz"]
['aaaw', 'aaax', 'aaay', 'aaaz']
>>> import math # per usare la sqrt() >>> [round(math.sqrt(x), 4) for x in range(1, 10)] # radici quadrate dei numeri da 1 a 9
[1.0, 1.4142, 1.7321, 2.0, 2.2361, 2.4495, 2.6458, 2.8284, 3.0]
>>> [(i, i ** 2, i ** 3) for i in (1, 5, 10)] # tuple con numero, quadrato e cubo
[(1, 1, 1), (5, 25, 125), (10, 100, 1000)]
Notiamo che:
return
di una funzione: provate a toglierle ed otterrete un errore).Le variabili contatore che creiamo all'interno delle parentesi quadre si comportano come le variabili locali delle funzioni: esse vengono create e distrutte all'interno della comprehension e non esistono al di fuori. Invece è possibile usare nella comprehension una variabile definita all'esterno di essa:
>>> c = 10 # definiamo una variabile >>> i = 5 # un'altra >>> [c * i * i for i in range(1, 10)]
[10, 40, 90, 160, 250, 360, 490, 640, 810]
>>> i # i e' rimasta invariata
5
La variabile i
usata all'interno della comprehension non ha modificato quella
preesistente. Nella documentazione ufficiale si fa notare che questo fatto permette di risparmiare memoria ed aumentare
la sicurezza (creando la lista con un ciclo for
la i
sarebbe stata invece modificata,
magari inavvertitamente).
>>> lettere = "abcdef"
>>> [2 * c for c in lettere]
>>> [lettere[i:] for i in range(len(lettere))]
>>> [(i + 1, lettere[i]) for i in range(len(lettere))]
ESERCIZIO 3.2: Definite (se non lo avete già fatto) la variabile lettere
come nell'esercizio precedente;
scrivete poi delle list comprehensions che diano come risultato le seguenti liste:
['a_a', 'b_b', 'c_c', 'd_d', 'e_e', 'f_f']
['a', 'bb', 'ccc', 'dddd', 'eeeee', 'ffffff']
[('abcdef', 1), ('bcdef', 2), ('cdef', 3), ('def', 4), ('ef', 5), ('f', 6)]
Vediamo ora cosa succede se scriviamo più di un for
: in questo caso essi si comportano come cicli
annidati, da sinistra a destra. Quindi:
l = [(x, y) for x in (1, 2, 3) for y in (1, 2, 3)]
è l'equivalente di:
l = []
for x in (1, 2, 3)
for y in (1, 2, 3)
l.append((x, y))
Ecco qualche esempio:
>>> [(x, y) for x in (1, 2, 3) for y in (1, 2, 3)]
[(1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (2, 3), (3, 1), (3, 2), (3, 3)]
>>> [(x, y) for x in (1, 2, 3) for y in range(1, x+1)]
[(1, 1), (2, 1), (2, 2), (3, 1), (3, 2), (3, 3)]
Notate che, nel secondo caso, abbiamo usato nel secondo for
il contatore x
del primo, cosa
possibile perchè esso è già stato definito (appunto nel primo for
).
[(1, a), (1, b), (2, a), (2, b)]
['ax', 'ay', 'bx', 'by', 'cx', 'cy', 'dx', 'dy']
[1, 2, 3, 4, 2, 4, 6, 8, 3, 6, 9 , 12]
Infine possiamo inserire dopo i for
delle istruzioni if
, tipicamente per escludere
qualche caso nell'esecuzione dei cicli.
>>> [i for i in range(1, 11) if i not in (3, 6)]
[1, 2, 4, 5, 7, 8, 9, 10]
>>> squadre = ["Milan", "Inter", "Juventus", "Roma"] >>> partite = [x + " - " + y for x in squadre for y in squadre if x != y] >>> partite
['Milan - Inter', 'Milan - Juventus', 'Milan - Roma', 'Inter - Milan', 'Inter - Juventus', 'Inter - Roma', 'Juventus - Milan', 'Juventus - Inter', 'Juventus - Roma', 'Roma - Milan', 'Roma - Inter', 'Roma - Juventus']
Python richiede che il primo if
segua almeno un for
(tenendo sempre presente
che le variabili che vi compaiono devono già essere state definite), in ogni caso per non sbagliare è prassi comune
mettere tutti gli if
alla fine, dopo i for
.
Le list comprehension con gli if
vengono spesso usate come filtri,
cioè per selezionare da una lista o dizionario una sottosequenza che contiene solo i dati che ci interessano (facendo quindi un
lavoro simile all'estrazione di dati da un database). Per capire meglio riprendiamo il nostro esempio dell'alunno di una scuola
memorizzato mediante un dizionario, fatto qui nella lezione precedente. Copiate
in IDLE questa (lunghissima) riga di codice:
lista_alunni = [
{"nome":"Mario", "cognome":"Rossi", "classe":"1A",
"voti":{"Italiano":6, "Matematica":5, "Inglese":7 }},
{"nome":"Lucia", "cognome":"Bianchi", "classe":"1B",
"voti":{"Italiano":7, "Matematica":8, "Inglese":8 }},
{"nome":"Luigi", "cognome":"Neri", "classe":"2A",
"voti":{"Italiano":5, "Matematica":4, "Inglese":6 }},
{"nome":"Laura", "cognome":"Verdi", "classe":"1A",
"voti":{"Italiano":7, "Matematica":8, "Inglese":7 }},
{"nome":"LPietro", "cognome":"Gialli", "classe":"1A",
"voti":{"Italiano":7, "Matematica":8, "Inglese":5 }}
]
Ho semplificato un po' l'esempio per non allungare troppo: potete comunque facilmente capire che la variabile
lista_alunni
contiene i dati di 5 studenti: nome, cognome, classe, e i voti in Italiano, Matematica
e Inglese (ovviamente in una vera scuola la lista potrebbe contenere centinaia di studenti).
Ora voglio sapere quali di questi alunni hanno avuto un'insufficienza in matematica: posso farlo in un'unica istruzione usando una list comprehension:
>>> [(x["nome"], x["cognome"]) for x in lista_alunni if x["voti"]["Matematica"] < 6 ]
[('Mario', 'Rossi'), ('Luigi', 'Neri')]
La mia comprehension ha creato una lista di tuple che contengono il nome e cognome dell'alunno, selezionando tutti gli alunni con voto in matematica inferiore a 6.
lista_alunni
modificate l'istruzione if
in
modo da ottenere:
x["voti"]
)
sia almeno 6; per avere l'elenco dei soli voti numerici ... guardate quiand
possiamo ottenere condizioni ancora più sofisticate:
Fine della lezione