Liste innestate in Python
Le liste innestate, comunemente chiamate "liste di liste", rappresentano un concetto fondamentale nella programmazione Python. Esse permettono di creare strutture dati multidimensionali, consentendo agli sviluppatori di gestire in modo efficiente dati complessi e organizzati.
In questa lezione vedremo come creare e manipolare liste innestate in Python, e come utilizzarle per risolvere problemi complessi. Vedremo anche qualche esempio pratico di applicazione delle liste innestate, come la rappresentazione di matrici e tabelle.
Cos'è una Lista Innestata?
Una lista innestata è una lista che contiene altre liste come elementi. In Python, le liste possono contenere elementi di qualsiasi tipo, incluso un'altra lista. La possibilità di creare una struttura gerarchica di liste è particolarmente utile quando si tratta di dati strutturati, come matrici, tabelle o elenchi di elementi correlati. La sintassi per creare una lista innestata è molto semplice:
lista_innestata = [[elemento1, elemento2], [elemento3, elemento4], [elemento5, elemento6]]
Ogni lista interna può contenere un numero diverso di elementi, e queste liste possono essere a loro volta innestate all'interno di altre liste, creando così strutture dati complesse a più livelli.
In Python è possibile creare liste di liste anche assegnando una lista ad un elemento di un'altra lista. Ad esempio, la seguente istruzione crea una lista innestata:
lista_interna = [elemento1, elemento2]
lista_esterna = [lista_interna, elemento3]
La lista di sopra lista_esterna
contiene due elementi: la lista lista_interna
e l'elemento elemento3
. La lista lista_interna
a sua volta contiene due elementi: elemento1
e elemento2
.
Ricapitolando:
Lista Innestata o Lista di Liste
Una lista innestata è una lista che contiene altre liste come elementi al proprio interno.
Si può definire una lista innestata o lista di liste in fase di creazione della lista dichiarando le liste interne tra parentesi quadre e separate da virgole:
lista_innestata = [[elemento1, elemento2], [elemento3, elemento4], [elemento5, elemento6]]
oppure assegnando una lista ad un elemento di un'altra lista:
lista_interna = [elemento1, elemento2]
lista_esterna = [lista_interna, elemento3]
Accesso agli elementi di una Lista Innestata
Per accedere agli elementi di una lista innestata bisogna tener presente che ogni elemento della lista esterna è a sua volta una lista. Per accedere agli elementi di una lista innestata bisogna quindi utilizzare due indici: il primo per accedere all'elemento della lista esterna, il secondo per accedere all'elemento della lista interna.
Chiariamo con un esempio. Consideriamo la seguente lista innestata:
lista_innestata = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Supponiamo di voler accedere all'elemento 5
della lista innestata. Per farlo, dobbiamo utilizzare due indici: il primo per accedere alla seconda lista, il secondo per accedere all'elemento 5
:
print(lista_innestata[1][1]) # Output: 5
In questo esempio, lista_innestata[1]
restituisce la seconda lista [4, 5, 6]
, mentre lista_innestata[1][1]
restituisce l'elemento 5
della seconda lista.
In sostanza l'indicizzazione in questo caso è di tipo gerarchico, e si può rappresentare come una struttura ad albero:
lista_innestata
|
├─[0]-------┬-[0]------1
| ├-[1]------2
| └-[2]------3
|
├─[1]-------┬-[0]------4
| ├-[1]------5
| └-[2]------6
|
└─[2]-------┬-[0]------7
├-[1]------8
└-[2]------9
Il discorso può essere esteso anche al caso in cui i livelli di innestamento siano più di due. Consideriamo la seguente lista innestata:
lista_innestata = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9, [10, 11, 12]]
]
Se rappresentiamo la lista innestata come un albero, otteniamo la seguente struttura:
lista_innestata
|
├─[0]-------┬-[0]------1
| ├-[1]------2
| └-[2]------3
|
├─[1]-------┬-[0]------4
| ├─[1]------5
| └─[2]------6
|
└─[2]-------┬-[0]------7
├─[1]------8
├─[2]------9
└─[3]------┬-[0]------10
├─[1]------11
└─[2]------12
Adesso, volendo accedere all'elemento 11
, dobbiamo utilizzare tre indici:
print(lista_innestata[2][3][1]) # Output: 11
Ricapitolando:
Indicizzazione Gerarchica
Per accedere agli elementi di una lista innestata bisogna utilizzare un numero di indici pari al numero di livelli di innestamento.
La sintassi per accedere agli elementi di una lista innestata è la seguente:
lista_innestata[indice1][indice2]...[indiceN]
Ogni indice è relativo ad un livello di innestamento.
Modifica di una Lista Innestata
Per modificare un elemento di una lista innestata bisogna utilizzare la sintassi per l'indicizzazione gerarchica:
lista_innestata[indice1][indice2]...[indiceN] = nuovo_valore
Consideriamo l'esempio precedente:
lista_innestata = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Supponiamo di voler modificare l'elemento 5
con il valore 10
. Per farlo, dobbiamo utilizzare due indici: il primo per accedere alla seconda lista, il secondo per accedere all'elemento 5
:
lista_innestata[1][1] = 10
print(lista_innestata) # Output: [[1, 2, 3], [4, 10, 6], [7, 8, 9]]
Eliminazione di elementi da una Lista Innestata
Anche l'eliminazione di un elemento da una lista innestata richiede l'utilizzo della sintassi per l'indicizzazione gerarchica:
del lista_innestata[indice1][indice2]...[indiceN]
Consideriamo l'esempio precedente:
lista_innestata = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Supponiamo di voler eliminare l'elemento 5
. Per farlo, dobbiamo utilizzare due indici: il primo per accedere alla seconda lista, il secondo per accedere all'elemento 5
:
del lista_innestata[1][1]
print(lista_innestata) # Output: [[1, 2, 3], [4, 6], [7, 8, 9]]
Aggiunta di elementi ad una Lista Innestata
Come nei casi precedenti, anche l'aggiunta di un elemento ad una lista innestata richiede l'utilizzo della sintassi per l'indicizzazione gerarchica:
lista_innestata[indice1][indice2]...[indiceN].append(nuovo_elemento)
Consideriamo l'esempio precedente:
lista_innestata = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Supponiamo di voler aggiungere l'elemento 10
alla terza lista. Per farlo, dobbiamo utilizzare due indici: il primo per accedere alla terza lista, il secondo per aggiungere l'elemento 10
:
lista_innestata[2].append(10)
print(lista_innestata) # Output: [[1, 2, 3], [4, 5, 6], [7, 8, 9, 10]]
A questo punto la terza lista interna conterrà quattro elementi anziché tre.
Applicazione Pratica: Matrici
Le matrici sono una struttura dati bidimensionale composta da righe e colonne, spesso utilizzate per rappresentare dati matematici. Le liste innestate sono un meccanismo naturale per la rappresentazione delle matrici in Python.
Consideriamo la seguente matrice:
La matrice può essere rappresentata in Python come una lista innestata:
matrice = [
[1, 2, 3], # Riga 0
[4, 5, 6], # Riga 1
[7, 8, 9] # Riga 2
]
Ogni lista interna rappresenta una riga della matrice, e gli elementi all'interno di ciascuna lista rappresentano i valori delle colonne corrispondenti.
Accedere agli elementi di una matrice è molto semplice: basta utilizzare due indici, uno per la riga e uno per la colonna, ricordando, però, che gli indici partono da zero. Per cui se vogliamo accedere all'elemento i-1
e j-1
.
Ad esempio, volendo accedere all'elemento 1
e 2
:
a23 = matrice[1][2]
print(a23) # Output: 6
Esempio: Somma di due Matrici
Supponiamo di voler sommare due matrici
Utilizzando liste innestate, possiamo facilmente implementare questa operazione. Per prima cosa, dobbiamo creare due liste innestate, una per la matrice
A = [
[1, 2, 3], # Riga 0
[4, 5, 6], # Riga 1
[7, 8, 9] # Riga 2
]
B = [
[9, 8, 7], # Riga 0
[6, 5, 4], # Riga 1
[3, 2, 1] # Riga 2
]
Successivamente possiamo definire una funzione che somma due matrici:
1 2 3 4 5 6 7 8 9 10 11 |
|
La funzione somma_matrici
prende in input due matrici
Per prima cosa, alla riga 3, viene creata la matrice risultato
Successivamente, viene effettuata la somma delle matrici for
nelle righe 6-8. Infine, viene restituita la matrice risultato
Per testare la funzione somma_matrici
, possiamo utilizzare il seguente codice:
C = somma_matrici(A, B, 3, 3)
print(C) # Output: [[10, 10, 10], [10, 10, 10], [10, 10, 10]]
Il problema di questo approccio è che la funzione somma_matrici
prende in ingresso le dimensioni delle matrici somma_matrici
utilizzando la funzione len
per calcolare le dimensioni delle matrici
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
La differenza, rispetto alla prima versione della funzione, è che la funzione somma_matrici_versione_2
non prende in ingresso le dimensioni delle matrici len
nelle righe 2-3. In particolare, per calcolare il numero di colonne della matrice len
sulla prima riga della matrice
Questo funziona fintanto che tutte le righe della matrice somma_matrici_versione_2
non funziona correttamente.
Adesso possiamo utilizzare la funzione somma_matrici_versione_2
in questo modo:
C = somma_matrici_versione_2(A, B)
print(C) # Output: [[10, 10, 10], [10, 10, 10], [10, 10, 10]]
Esempio: Prodotto di una Matrice per uno Scalare
Supponiamo di voler moltiplicare una matrice
Possiamo implementare questa operazione utilizzando una funzione:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
La funzione prodotto_matrice_scalare
prende in input una matrice
Adesso possiamo utilizzare la funzione prodotto_matrice_scalare
in questo modo:
B = prodotto_matrice_scalare(A, 2)
print(B) # Output: [[2, 4, 6], [8, 10, 12], [14, 16, 18]]
Applicazione Pratica: Tabelle
Le liste innestate trovano la loro naturale applicazione anche nella rappresentazione di tabelle di dati.
Supponiamo, ad esempio, di voler memorizzare in una tabella i dati relativi alle principali città italiane. Vogliamo memorizzare in una lista i seguenti dati:
- Nome della città
- Regione
- Numero di abitanti
- Superficie in
km^2
Possiamo rappresentare la tabella utilizzando una lista innestata:
citta = [
["Milano", "Lombardia", 1352000, 181.76],
["Roma", "Lazio", 2873000, 1285.31],
["Napoli", "Campania", 967000, 119.00],
["Torino", "Piemonte", 886837, 130.17],
["Palermo", "Sicilia", 673735, 158.90],
["Genova", "Liguria", 583601, 243.60],
["Bologna", "Emilia-Romagna", 388367, 140.70],
["Firenze", "Toscana", 382808, 102.41],
["Bari", "Puglia", 325052, 116.00],
["Catania", "Sicilia", 311584, 180.90]
]
Per accedere ai dati relativi alla città di Milano, possiamo utilizzare la seguente sintassi:
milano = citta[0]
print(milano) # Output: ['Milano', 'Lombardia', 1352000, 181.76]
Supponiamo di voler ricavare la densità di popolazione di ciascuna città. Possiamo utilizzare la seguente funzione:
1 2 3 4 5 6 |
|
Adesso possiamo utilizzare la funzione densita_di_popolazione
in questo modo:
for c in citta:
print(f'{c[0]:20} {densita_di_popolazione(c)}')
Il codice precedente stampa la densità di popolazione di ciascuna città:
Milano 7431.986531986532
Roma 2234.422657952069
Napoli 8134.453781512605
Torino 6814.563106796117
Palermo 4240.911949685534
Genova 2396.296296296296
Bologna 2759.014209591474
Firenze 3739.100049390681
Bari 2801.448275862069
Catania 1720.4339963833635
In Sintesi
Le liste innestate in Python sono uno strumento potente per la gestione delle matrici e altre strutture dati bidimensionali. La loro capacità di organizzare dati in modo gerarchico e la facilità con cui consentono di accedere agli elementi li rendono essenziali per affrontare problemi complessi che coinvolgono dati tabellari, immagini e algoritmi matematici.
In questa lezione abbiamo visto:
- Come creare una lista innestata
- Come accedere agli elementi di una lista innestata
- Come modificare gli elementi di una lista innestata
- Come creare una matrice utilizzando una lista innestata
- Come creare tabelle di dati utilizzando liste innestate