Liste, Tuple e Dizionari in Python (questione di parentesi): Guida Completa con Esercizi Pratici

Cerca:

Generic selectors
Exact matches only
Search in title
Search in content
Post Type Selectors
Liste, Tuple, Dizionari Le Basi Python

Python, un linguaggio di programmazione potente e versatile, offre diverse strutture dati fondamentali per organizzare e manipolare le informazioni. Tra queste, liste, tuple e dizionari sono senza dubbio le più usate. Spesso però, sorge un dubbio comune tra chi si avvicina a Python: quali parentesi usare e perché?

Non è raro confondersi tra le parentesi quadre [], le tonde () e le graffe {}, ognuna con un ruolo ben preciso che definisce non solo come si creano queste strutture, ma anche il loro comportamento e le loro applicazioni pratiche. Comprendere a fondo queste distinzioni è cruciale per scrivere codice efficiente, leggibile e privo di errori.

In questa guida, esploreremo le caratteristiche uniche di liste, tuple e dizionari, chiarendo una volta per tutte le differenze nelle loro definizioni, mutabilità e casi d’uso. Preparati a sciogliere ogni dubbio e a padroneggiare queste fondamenta essenziali di Python!

1. Liste → parentesi quadre [ ]

Definizione: ordinata, mutabile (modificabile), può contenere duplicati.

Sintassi:


lista = [1, 2, 3, 4]

Esempi:


lista.append(5)
print(lista)  # [1, 2, 3, 4, 5]

 

2. Tuple → parentesi tonde ( )

Definizione: ordinata, immutabile (non modificabile), può contenere duplicati.

Sintassi:


tupla = (1, 2, 3)

Nota: una tupla con un solo elemento ha bisogno della virgola:


tupla_singola = (5,)  # È una tupla
non_tupla = (5)       # È un intero

 

 3. Dizionari → parentesi graffe { }

Definizione: collezione non ordinata (in Python 3.6+ è ordinata nell’inserimento), mutabile, basata su coppie chiave-valore.

Sintassi:


dizionario = {"nome": "Anna", "età": 30}

Accesso:


print(dizionario["nome"])  # Anna

Riepilogo visivo

Tipo Parentesi Esempio
Lista [ ] [1, 2, 3]
Tupla ( ) (1, 2, 3)
Dizionario { } {“a”: 1, “b”: 2}

 

Curiosità

Anche i set (insiemi) usano le graffe { }, ma senza coppie chiave-valore:


insieme = {1, 2, 3}

Esercizi su Liste, Tuple e Dizionari in Python

Ecco una serie di esercizi graduati (con soluzioni) per aiutarti a distinguere e usare correttamente liste, tuple e dizionari in Python, con particolare attenzione alle parentesi usate.

 

Esercizi – Livello base

🔹 Esercizio 1: Riconosci la struttura

Indica se ogni elemento è una lista, una tupla o un dizionario:


a = [10, 20, 30]
b = (10, 20, 30)
c = {"nome": "Sara", "età": 25}
d = {"a", "b", "c"}
e = (50,)
f = [100]

👉 Soluzione:

  • a → lista
  • b → tupla
  • c → dizionario
  • d → insieme (set) → attenzione: graffe senza chiavi
  • e → tupla (singolo elemento)
  • f → lista

Concetti Chiave e Approfondimenti:

Sintassi è tutto: Questo esercizio è fondamentale per instillare la memoria visiva della sintassi in Python. Le parentesi non sono un dettaglio, ma la firma stessa del tipo di struttura dati.

set con le graffe (senza chiavi): Un punto critico che spesso confonde i principianti. Le graffe {} non indicano sempre un dizionario. Se contengono solo valori separati da virgole, senza la sintassi chiave: valore, allora si tratta di un set. Questa distinzione è vitale!

Tupla con singolo elemento (5,): Un altro “trabocchetto” classico. Python interpreta (5) come un’espressione aritmetica (l’intero 5), non come una tupla. La virgola finale (5,) è l’unico modo per dire a Python che si vuole creare una tupla di un solo elemento. Questo sottolinea l’importanza di dettagli sintattici apparentemente minori.

Liste per elementi singoli [100]: A differenza delle tuple, una lista con un singolo elemento non ha bisogno di “trucchi” sintattici. [100] è una lista valida senza la virgola aggiuntiva, evidenziando una delle piccole asimmetrie nel design del linguaggio.

🔹 Esercizio 2: Completa il codice

Scrivi una struttura che:

  • Contenga tre frutti
  • Sia modificabile
  • Permetta accesso per indice

👉 Soluzione:


frutti = ["mela", "banana", "kiwi"]  # lista
print(frutti[1])  # banana
frutti.append("pera")

Concetti Chiave e Approfondimenti:

Mutabilità e accessibilità per indice: La richiesta indirizza direttamente verso la lista. Questo esercizio rinforza il concetto chiave di mutabilità delle liste (possiamo aggiungere append o modificare elementi dopo la creazione) e la loro natura ordinata che permette l’accesso tramite indice numerico (es. frutti[1]).

append() metodo: Un metodo fondamentale delle liste. Dimostra come le liste siano dinamiche e possano crescere o ridursi in dimensione.

🔹 Esercizio 3: Immutabilità

Prova a modificare questi dati. Cosa succede?


t = (1, 2, 3)
t[0] = 10

👉 Soluzione:

Errore! Le tuple non possono essere modificate. TypeError: 'tuple' object does not support item assignment

Concetti Chiave e Approfondimenti:

TypeError: 'tuple' object does not support item assignment: Questo è l’errore classico che si ottiene quando si tenta di modificare una tupla. È il modo più diretto per imparare il concetto di immutabilità. Le tuple sono “scatole” sigillate: una volta create, il loro contenuto non può essere alterato (aggiunto, rimosso o modificato in-place).

Quando usare le tuple?: Sebbene limitante per la modifica, l’immutabilità è una caratteristica potente.

Rende le tuple ideali per:

Record fissi: Dati che non dovrebbero cambiare (coordinate geografiche, date di nascita, chiavi composite).

Sicurezza del dato: Garantisce che il dato non venga modificato accidentalmente altrove nel codice.

Chiavi di dizionario: Solo tipi immutabili (come tuple, stringhe, numeri) possono essere usati come chiavi di dizionario, perché la loro “identità” non deve cambiare.

Forse potrebbe interessarti anche:  Catene di Markov in Python: Modellare Funnel, Retention e Churn oltre le classiche dashboard

Esercizi – Livello intermedio

🔹 Esercizio 4: Crea una rubrica telefonica

Usa un dizionario per associare nomi a numeri di telefono.


rubrica = {
    "Alice": "333-1234567",
    "Bob": "328-7654321"
}

Aggiungi un nuovo contatto:


rubrica["Carlo"] = "392-1122334"

Concetti Chiave e Approfondimenti:

Dizionario come mappatura: L’esempio della rubrica è l’archetipo dell’uso di un dizionario. Ogni “nome” è una chiave univoca che punta a un “numero di telefono” (il valore). Questo è il loro scopo primario: associare un valore a una chiave per un recupero rapido.

Accesso e aggiunta tramite chiave: rubrica["Carlo"] = "392-1122334" dimostra la mutabilità e la flessibilità dei dizionari. Se la chiave “Carlo” non esiste, viene aggiunta una nuova coppia chiave-valore. Se esistesse già, il valore verrebbe aggiornato. Questo rende i dizionari estremamente versatili per la gestione di dati dinamici.

Non ordinato (storicamente): Sebbene Python 3.7+ garantisca l’ordine di inserimento, è utile ricordare che in versioni precedenti e concettualmente, i dizionari erano visti come collezioni non ordinate. L’accesso avviene sempre tramite chiave, non tramite posizione.

🔹 Esercizio 5: Estrai dati

Data la struttura:


persona = ("Luca", 30, "Milano")

Stampa la città.

👉 Soluzione:


print(persona[2])  # Milano

Concetti Chiave e Approfondimenti:

Accesso per indice nelle tuple: Nonostante siano immutabili, le tuple sono ordinate e quindi supportano l’accesso tramite indice, proprio come le liste. persona[2] è un modo efficiente e diretto per recuperare il terzo elemento.

packing e unpacking (implicito): Quando si crea persona = ("Luca", 30, "Milano"), si sta facendo un packing di valori in una tupla. L’accesso per indice è un modo per “disfare” parzialmente la tupla. L’unpacking completo sarebbe nome, età, città = persona.

🔹 Esercizio 6: Modifica un valore

Hai una lista di prezzi:


prezzi = [10.5, 8.9, 12.0]

Modifica il secondo prezzo portandolo a 9.9.

👉 Soluzione:


prezzi[1] = 9.9

Concetti Chiave e Approfondimenti:

Mutabilità delle liste in azione: prezzi[1] = 9.9 è l’esempio più semplice e chiaro di come modificare un elemento in una lista. Rinforza il concetto che le liste sono strutture dinamiche e modificabili “in-place”.

Indici base zero: Ricorda sempre che in Python (e nella maggior parte dei linguaggi di programmazione), gli indici delle sequenze iniziano da 0. Quindi, il “secondo” elemento è all’indice 1.

🔹 Esercizio 7: Quale struttura usare?

Per ogni situazione, scegli la struttura più adatta (lista, tupla o dizionario):

Situazione Struttura consigliata
Ordinare libri in una libreria Lista
Coordinate geografiche (lat, long) Tupla
Mappare utenti ed email Dizionario

Concetti Chiave e Approfondimenti:

Pensare al problema, non solo alla sintassi: Questo esercizio sposta l’attenzione dalla mera sintassi all’applicazione pratica e alla scelta strategica. È il cuore della programmazione: capire quale strumento è il migliore per un dato compito.

Libreria (ordinare libri): Lista: I libri in una libreria spesso hanno un ordine (es. alfabetico, per genere) e la collezione può cambiare (nuovi acquisti, prestiti). La mutabilità e l’ordine delle liste sono perfetti.

Coordinate geografiche (lat, long): Tupla: Le coordinate sono un insieme di valori fissi e immutabili. Una volta definite, latitudine e longitudine di un punto non cambiano. L’immutabilità della tupla garantisce che queste coppie di valori non vengano alterate accidentalmente. Inoltre, le tuple sono hashable (a differenza delle liste), il che significa che potrebbero essere usate come chiavi in un dizionario, se necessario (es. mappa_punti = {(40.71, -74.00): "New York"}).

Mappare utenti ed email: Dizionario: Qui è necessaria un’associazione univoca. L’utente è la chiave, l’email è il valore. Il recupero dell’email basato sull’utente (o viceversa, se si usasse un dizionario inverso) è rapidissimo.

Sfida finale

🔹 Esercizio 8: Combinazione

Definisci una struttura dati che rappresenti una classe scolastica, dove:

  • Ogni studente ha un nome e un’età
  • Gli studenti sono memorizzati in un elenco

👉 Soluzione:


classe = [
    {"nome": "Anna", "età": 15},
    {"nome": "Marco", "età": 16},
    {"nome": "Giulia", "età": 15}
]

Accedi all’età del secondo studente:


print(classe[1]["età"])  # 16

Concetti Chiave e Approfondimenti:

Strutture dati annidate: Questo è un concetto cruciale. Le strutture dati possono essere annidate all’infinito, creando gerarchie complesse ma altamente strutturate. Qui abbiamo una lista di dizionari, che è una combinazione potentissima:

La lista esterna classe = [...] rappresenta una collezione ordinata di studenti (ogni studente è un elemento). La lista è mutabile, quindi possiamo aggiungere o rimuovere studenti.

Ogni dizionario interno {"nome": "Anna", "età": 15} rappresenta un singolo studente. Il dizionario è ideale perché ogni studente ha diverse proprietà (nome, età) che sono accessibili tramite una chiave descrittiva. Il dizionario è mutabile, quindi possiamo modificare l’età di uno studente.

Accesso a elementi annidati: print(classe[1]["età"]) è un esempio perfetto di come si naviga in strutture annidate. Prima si accede all’elemento della lista tramite indice (classe[1]), che restituisce un dizionario. Poi si accede al valore all’interno di quel dizionario tramite chiave (["età"]). Questo tipo di accesso “a catena” è comune in Python quando si lavora con dati complessi (es. dati JSON).

Forse potrebbe interessarti anche:  Algoritmi Greedy: Una Scelta Ottimale (a Volte)

Esercizi “da Hero” su Liste, Tuple e Dizionari

Ecco 4 esercizi “da hero” che ti aiuteranno a consolidare la tua comprensione e l’uso di liste, tuple e dizionari in Python.

Questi esercizi sono pensati per sfidarti con logica, manipolazione avanzata e per farti riflettere sulle differenze e i casi d’uso ottimali di ciascuna struttura dati.

( In fondo trovi le soluzioni 😉 😉)

Esercizio 1 – Tupla o lista? Questione di scelta

Hai ricevuto dei dati da un sensore sotto forma di tuple:


dati_sensore = [(10, 20), (15, 25), (12, 22)]

Scrivi una funzione delta_max_min() che calcoli la differenza tra il valore massimo e minimo di ciascuna coppia. Il risultato deve essere una lista di valori.

👉 Output atteso:


[10, 10, 10]

Suggerimento: Usa map() oppure una list comprehension.

Esercizio 2 – Dizionario annidato intelligente

Hai questo dizionario:


studenti = {
    "Anna": {"matematica": 8, "italiano": 7},
    "Marco": {"matematica": 6, "italiano": 9},
    "Giulia": {"matematica": 9, "italiano": 6}
}

Scrivi una funzione che restituisca il nome dello studente con la media più alta. La funzione deve funzionare per qualsiasi numero di materie e studenti.

👉 Output atteso:


Giulia

Suggerimento: Usa sum(), len(), max() con key.

Esercizio 3 – Dizionario inverso con lista di tuple

Hai una lista di tuple dove ogni elemento rappresenta una coppia (corso, docente):


corsi = [
    ("Matematica", "Prof. Rossi"),
    ("Fisica", "Prof. Verdi"),
    ("Matematica", "Prof. Bianchi"),
    ("Chimica", "Prof. Verdi")
]

Costruisci un dizionario inverso in cui le chiavi sono i nomi dei docenti e i valori sono le liste dei corsi insegnati da ciascuno.

👉 Output atteso:


{
    "Prof. Rossi": ["Matematica"],
    "Prof. Verdi": ["Fisica", "Chimica"],
    "Prof. Bianchi": ["Matematica"]
}

Suggerimento: Usa defaultdict(list) oppure controlla se la chiave esiste con un if.

Esercizio 4 – Deep destructuring

Hai la seguente struttura complessa:


dati = [
    ("Luca", (1990, "Milano")),
    ("Sara", (1985, "Roma")),
    ("Paolo", (2000, "Torino"))
]

Estrai solo le città e salvale in una lista.

Estrai i nomi delle persone nate prima del 1990 in una lista chiamata over30.

👉 Output atteso:


["Milano", "Roma", "Torino"]
["Sara"]

Suggerimento: Usa unpacking annidato dentro un ciclo for.


Pubblicità

 

✅ Soluzioni esercizi “da hero”

Ecco le soluzioni passo passo per i 4 esercizi, ognuna corredata da commenti esplicativi per chiarire le differenze tra liste, tuple e dizionari, insieme alle logiche Python coinvolte.

 

 Esercizio 1 – delta_max_min()

Obiettivo: per ogni tupla, calcolare la differenza tra massimo e minimo → output come lista.


dati_sensore = [(10, 20), (15, 25), (12, 22)]

def delta_max_min(lista_tuple):
    # List comprehension per iterare su ogni tupla e calcolare max - min
    return [max(coppia) - min(coppia) for coppia in lista_tuple]

print(delta_max_min(dati_sensore))

Output:


[10, 10, 10]

🔍 Commento:

  • Tuple per dati immutabili: dati_sensore = [(10, 20), ...] è un esempio eccellente di dove le tuple sono preferibili. Le letture di un sensore sono dati “grezzi” che non dovrebbero essere alterati dopo essere stati acquisiti. Usare tuple garantisce l’integrità del dato sorgente.
  • List Comprehension [espressione for elemento in iterabile]: Questa è una delle caratteristiche più potenti ed “idiomatiche” di Python. Permette di creare nuove liste in modo conciso e leggibile. Al posto di un ciclo for con append, si genera direttamente la lista desiderata.
# Alternativa meno concisa (ma altrettanto valida)
risultati = []
for coppia in lista_tuple:
    risultati.append(max(coppia) - min(coppia))
return risultati
  • max() e min() su sequenze: Funzioni built-in versatili che operano su qualsiasi iterabile (liste, tuple, ecc.), rendendo il calcolo del delta molto semplice.
  • La flessibilità dell’output (lista): Sebbene l’input sia una lista di tuple (immutabili), la richiesta è una lista di valori. Questo mostra come si possano combinare diverse strutture dati per adattarsi meglio alle esigenze del problema: input fisso e sicuro, output modificabile e facilmente manipolabile.

Esercizio 2 – Media migliore in dizionario annidato


studenti = {
    "Anna": {"matematica": 8, "italiano": 7},
    "Marco": {"matematica": 6, "italiano": 9},
    "Giulia": {"matematica": 9, "italiano": 6}
}

def migliore_media(studenti):
    def media(voti):
        return sum(voti.values()) / len(voti)
    
    # max su dizionario con chiave personalizzata
    return max(studenti.items(), key=lambda item: media(item[1]))[0]

print(migliore_media(studenti))

Output:


Giulia

🔍 Commento:

  • Dizionari annidati per dati strutturati: studenti = {"Anna": {"matematica": 8, ...}} è un pattern comune per rappresentare dati complessi dove ogni record ha sotto-attributi (es. voti per materia). Il dizionario esterno mappa i nomi degli studenti (chiavi uniche) a un altro dizionario che contiene i voti (chiave-valore).
  • studenti.items(): Fondamentale per iterare su chiavi e valori di un dizionario. Restituisce una sequenza di tuple (chiave, valore), in questo caso ("Anna", {"matematica": 8, "italiano": 7}).
  • Funzioni sum() e len(): Usate per calcolare la media dei voti. voti.values() restituisce una “vista” sui valori (i voti numerici) del dizionario annidato, permettendo a sum() di sommarli e len() di contare quante materie ci sono.
  • max() con key=lambda: Questo è il vero “pezzo forte” dell’esercizio.
    • max() trova l’elemento più grande in un iterabile.
    • La magia è nel parametro key: invece di confrontare direttamente gli elementi, max() applica la funzione specificata da key a ogni elemento e confronta i risultati di quella funzione.
    • lambda item: media(item[1]): Una funzione anonima (lambda) che prende un item (che è una tupla (nome, dizionario_voti) restituita da studenti.items()) e ne calcola la media. item[1] accede al dizionario dei voti. max() userà queste medie per decidere qual è l’elemento “massimo”, ma restituirà l’elemento originale (la tupla (nome, dizionario_voti)).
    • [0]: Infine, dopo che max() ha trovato la tupla dello studente con la media più alta, si accede al primo elemento di quella tupla, che è il nome dello studente.
  • Questo esercizio dimostra come Python permetta di scrivere codice molto compatto ed espressivo per problemi complessi, sfruttando funzioni built-in e lambdas.
Forse potrebbe interessarti anche:  Guida a Pytrends in Python: estrarre dati da Google Trends e gestire l'errore 429

 

Esercizio 3 – Dizionario inverso


from collections import defaultdict

corsi = [
    ("Matematica", "Prof. Rossi"),
    ("Fisica", "Prof. Verdi"),
    ("Matematica", "Prof. Bianchi"),
    ("Chimica", "Prof. Verdi")
]

def inverti_corsi(lista_tuple):
    docenti = defaultdict(list)
    for corso, prof in lista_tuple:
        docenti[prof].append(corso)
    return dict(docenti)

print(inverti_corsi(corsi))

Output:


{
    "Prof. Rossi": ["Matematica"],
    "Prof. Verdi": ["Fisica", "Chimica"],
    "Prof. Bianchi": ["Matematica"]
}

🔍 Commento:

  • Usiamo defaultdict(list) per evitare di dover controllare se una chiave (il nome del professore) esiste già nel dizionario prima di aggiungere un corso alla sua lista di corsi. Se la chiave non esiste, defaultdict la crea automaticamente con una lista vuota. Senza defaultdict, si dovrebbe fare così:
docenti = {}
for corso, prof in lista_tuple:
    if prof not in docenti:
        docenti[prof] = [] # Se la chiave non esiste, inizializza una lista vuota
    docenti[prof].append(corso)

  • defaultdict(list) automatizza il controllo dell’esistenza della chiave e l’inizializzazione del valore a una lista vuota. Quando si tenta di accedere a una chiave inesistente, defaultdict la crea automaticamente e le assegna il valore predefinito specificato (in questo caso, una lista vuota []). Questo riduce drasticamente il codice e lo rende più leggibile.
  • Iterazione e Unpacking di tuple: for corso, prof in lista_tuple: è un esempio pulito di unpacking diretto durante l’iterazione. Ogni tupla ("Matematica", "Prof. Rossi") viene automaticamente “smontata” nelle variabili corso e prof.
  • Costruire relazioni “uno-a-molti”: Il problema richiede di mappare un professore a molti corsi. Le liste come valori del dizionario sono perfette per questo scopo, permettendo di memorizzare collezioni di elementi associati a una singola chiave.
  • La lista di tuple come input (corsi) è una struttura leggera e ordinata per rappresentare le associazioni corso-docente.
  • L’output è un dizionario, ideale per strutture a chiave univoca (il professore), con valori che sono liste (i corsi che insegna).

 

Esercizio 4 – Deep destructuring


dati = [
    ("Luca", (1990, "Milano")),
    ("Sara", (1985, "Roma")),
    ("Paolo", (2000, "Torino"))
]

# Estrazione delle città
città = [città for _, (_, città) in dati]
print(città)

# Nomi nati prima del 1990
over30 = [nome for nome, (anno, _) in dati if anno < 1990]
print(over30)

Output:


['Milano', 'Roma', 'Torino']
['Sara']

🔍 Commento:

  • Unpacking annidato nelle List Comprehensions: Questo è il clou dell’esercizio e un segno di vera padronanza. L’espressione nome, (anno, città) in dati nel ciclo della list comprehension permette di “aprire” simultaneamente la tupla esterna e la tupla interna.
    • nome: prende il primo elemento della tupla esterna.
    • (anno, città): prende il secondo elemento della tupla esterna, che a sua volta è una tupla, e la scompone ulteriormente in anno e città.
  • L’uso dell’underscore _ per ignorare valori: Quando si fa unpacking e non si ha bisogno di un particolare valore, si può usare _ (underscore). Ad esempio, in (_, città), l’underscore indica che il primo elemento della tupla interna (l’anno) viene ignorato. Similmente, in (anno, _), la città viene ignorata. Questo rende il codice più pulito e indica chiaramente l’intenzione di non utilizzare quel valore.
  • Filtri condizionali nelle List Comprehensions (if clause): if anno < 1990 dimostra come le list comprehension non siano solo per la trasformazione, ma anche per il filtraggio degli elementi. Solo gli elementi che soddisfano la condizione vengono inclusi nella nuova lista.
  • Complessità dei dati reali: Questo esercizio simula scenari di dati più realistici, dove le informazioni sono strutturate in modo gerarchico. Essere in grado di navigare e manipolare tali strutture in modo efficiente è una competenza fondamentale.

 Conclusione

Per riassumere le differenze chiave tra queste strutture dati fondamentali di Python:

Struttura Parentesi Mutabilità Uso tipico
Lista [ ] ✅ sì Collezioni ordinabili e modificabili
Tupla ( ) ❌ no Record fissi (es. coordinate, eventi), dati che non devono cambiare
Dizionario { } ✅ sì Mappatura chiave-valore, dati strutturati per accesso rapido per chiave
Set { } (solo valori) ✅ sì Collezioni non ordinate, senza duplicati, utili per operazioni su insiemi
Pubblicità