Corso Python / Dizionari: chiavi, valori e totali per categoria

Un dizionario Python associa chiavi a valori e permette di trovare un dato tramite il suo nome. È utile per campi di un record e totali per categoria; devi però decidere se una chiave assente rappresenta un errore oppure un caso per cui usare un valore predefinito.

Capire chiavi, valori e sostituzioni

Un dizionario si scrive con coppie chiave: valore fra parentesi graffe. Assegnare di nuovo un valore alla stessa chiave sostituisce il precedente: non crea una seconda occorrenza della chiave.

expense = {"name": "lunch", "amount": 8000}
expense["amount"] = 9000
print(expense["amount"])

Il risultato è 9000. Il dizionario conserva l’ordine di inserimento, ma non ordina automaticamente le chiavi in ordine alfabetico né i valori in ordine numerico. Se l’output richiede un ordine particolare, specificalo come nel programma seguente. Aggiornare una chiave esistente non la sposta in fondo.

Le chiavi devono essere hashable: devono fornire un hash stabile durante la propria vita e, quando due oggetti risultano uguali, devono avere lo stesso hash. Stringhe e interi sono scelte comuni; una lista non è hashable, e nemmeno una tupla che contiene una lista. La definizione ufficiale è nel glossario Python: hashable. La guida alle tuple e alla loro immutabilità chiarisce perché il contenitore esterno non basta.

Chiavi stringa come "food", "Food" e "food " sono distinte. Scegli una regola di normalizzazione solo se queste differenze non hanno significato nei tuoi dati.

Calcolare totali per categoria

Salva il seguente programma completo in UTF-8 come dicts.py, in una cartella di esercitazione. Esegui python dicts.py con l’interprete del progetto selezionato. Su macOS o Ubuntu, fuori dall’ambiente virtuale, usa python3 se è il comando dell’interprete scelto. Non occorrono librerie esterne.

expenses = [("food", 8000), ("transport", 2500), ("food", 3500)]
totals = {}
for category, amount in expenses:
    totals[category] = totals.get(category, 0) + amount
for category, total in sorted(totals.items()):
    print(category, total)
print(totals.get("books", 0))

Il risultato atteso è:

food 11500
transport 2500
0

expenses contiene coppie categoria-importo. Il primo ciclo le separa con l’unpacking; per ogni categoria legge il totale precedente, parte da zero se ancora assente e aggiunge l’importo. Le due spese food diventano 11500; transport resta 2500.

totals.items() fornisce coppie chiave-valore. Qui sorted() ordina quelle coppie per categoria, perché le chiavi sono stringhe distinte, senza cambiare l’ordine del dizionario. L’ultima stampa restituisce zero per books, che non compare nelle spese.

Scegliere accesso obbligatorio o valore predefinito

totals["books"] genera KeyError se la chiave manca. totals.get("books", 0) restituisce invece 0 senza aggiungere la chiave al dizionario. Senza il secondo argomento, get() restituisce None quando la chiave non esiste. Il valore predefinito vale solo per una chiave assente, non sostituisce un valore None già presente.

Nell’accumulo partire da zero è appropriato per la prima spesa di una nuova categoria. Per un campo obbligatorio, però, sostituire silenziosamente l’assenza con zero può nascondere un record incompleto. Controlla la presenza con "books" in totals se devi distinguere «categoria assente» da «categoria presente con totale zero».

Per attraversare le chiavi usa for key in totals, per i valori values() e per le coppie items(). I metodi e l’ordine di iterazione sono descritti nella documentazione del tipo dict. Una copia con dict.copy() è superficiale: un’eventuale lista contenuta come valore rimane condivisa con l’originale.

Se ripeti spesso lo schema «chiave assente, totale iniziale zero», puoi valutare defaultdict(int) del modulo standard collections. Non è però identico a get(): l’accesso con parentesi a una chiave mancante crea una voce usando la factory. Scegli se questo inserimento sia desiderato prima di cambiare struttura.

Verificare categorie e modifiche durante l’iterazione

  1. Cambia soltanto la spesa food da 3500 a 4500: il totale di food deve diventare 12500 e quello di transport deve restare 2500.
  2. Ripristina l’importo e cambia una sola categoria da food a Food. Compaiono due gruppi distinti: non è un errore di somma, ma una differenza fra chiavi.
  3. Verifica che books resti assente dopo get("books", 0). Se la categoria è obbligatoria, segnala la mancanza invece di interpretare lo zero come una spesa realmente registrata.
  4. Ripristina i dati iniziali e confronta tutte le righe con il risultato atteso.

Aggiungere o eliminare chiavi mentre attraversi lo stesso dizionario può generare RuntimeError o rendere incompleta l’iterazione. Raccogli prima le chiavi da trattare in una lista separata oppure costruisci un nuovo dizionario. Il valore predefinito semplifica il calcolo, ma non deve decidere al posto tuo se i dati mancanti sono validi.