Dizionari — coppie di chiave e valore
Salvare e recuperare valori per nome, evitare il KeyError con .get(), aggiungere ed eliminare chiavi, percorrere il dizionario con .items() e perché una lista non può essere una chiave.
Come vuoi imparare?
Il problema che stiamo risolvendo
Servono tre voti da conservare — sapendo di chi è ognuno.
Le liste offrono un solo modo: due liste, affiancate.
names = ["rafi", "ahmed", "bilal"]
marks = [72, 45, 90]
print(names[1], marks[1])ahmed 45Funziona. E tutta l'impalcatura regge sulla tua disciplina, senza alcun aiuto da parte di Python.
Aggiungi qualcuno a names e dimentichi marks? Le due liste ora hanno lunghezze diverse, e da lì names[2] e marks[2] descrivono due persone diverse. Python non dirà una parola. Ordini names? La relazione si rompe del tutto, e in silenzio.
Qui sta il punto: la relazione tra le due cose non è scritta da nessuna parte. La relazione è solo "lo stesso indice" — una coincidenza che può rompersi in qualsiasi momento.
Un dizionario scrive la relazione nel codice. I nomi al posto degli indici, e la coppia tenuta insieme in un unico posto.
Alla fine di questo capitolo sarai in grado di
- Costruire un dizionario e recuperare un valore dalla sua chiave
- Evitare un
KeyErrorcon.get(), e sapere quando è giusto l'uno o l'altro - Aggiungere, cambiare e eliminare chiavi
- Percorrere un dizionario con
.keys(),.values()e.items() - Spiegare perché una lista non può essere una chiave mentre una tupla sì
Prerequisiti: Tuple e unpacking.
Un dizionario — coppie di chiave e valore
Parentesi graffe, con delle coppie key: value dentro:
marks = {"rafi": 72, "ahmed": 45, "bilal": 90}
print(marks)
print(marks["ahmed"])
print(len(marks)){'rafi': 72, 'ahmed': 45, 'bilal': 90}
45
3marks["ahmed"] — un nome, non un indice. E il nome è proprio lì nel codice, così sei mesi dopo dice ancora cosa si sta chiedendo.
len() conta le coppie: tre.
Una parola sull'ordine. Da Python 3.7 un dizionario ricorda l'ordine in cui le cose sono state inserite, e le stampa in quell'ordine. Ma un dizionario serve per cercare per nome, non per posizione — scrivimarks[0]e Python cercherà una chiave che sia il numero zero, non la troverà, e si fermerà conKeyError: 0.
Una chiave che non c'è — KeyError e .get()
marks = {"rafi": 72}
print(marks["dia"])KeyError: 'dia'Un KeyError significa "non esiste una chiave con quel nome". Lo stesso errore su una lista dava un IndexError — un nome diverso per lo stesso tipo di errore.
Per avere una risposta sicura invece di fermarti, usa .get():
marks = {"rafi": 72}
print(marks.get("dia"))
print(marks.get("dia", 0))
print(marks.get("rafi", 0))None
0
72Il secondo argomento dice "dammi questo se manca". Senza argomento ottieni None.
Quale, quando? Se la chiave ci deve essere, scrivi marks["rafi"] — la sua assenza è un bug, e il KeyError te lo consegna subito. Se il fatto che manchi è normale, usa .get(). Usare .get() dappertutto lascia passare in silenzio i dati sbagliati, e i dati sbagliati erano proprio ciò che stavi cercando.
Aggiungere, cambiare, eliminare
marks = {"rafi": 72}
marks["dia"] = 88
marks["rafi"] = 75
print(marks)
del marks["rafi"]
print(marks){'rafi': 75, 'dia': 88}
{'dia': 88}La stessa riga fa due lavori: se la chiave manca viene aggiunta, se esiste viene rimpiazzata. Non esiste un metodo "aggiungi" separato.
E da qui nasce una regola importante — una chiave può comparire una sola volta:
marks = {"rafi": 72, "rafi": 90}
print(marks){'rafi': 90}Nessun errore. La seconda ha cancellato la prima in silenzio. Succede regolarmente quando si caricano dati da un file, ed è un modo comune in cui i record spariscono.
in cerca le chiavi, non i valori
marks = {"rafi": 72, "ahmed": 45}
print("rafi" in marks)
print(72 in marks)True
False72 sicuramente c'è come valore, ma in cerca solo tra le chiavi. Per cercare tra i valori devi dirlo: 72 in marks.values().
Percorrerlo, in tre modi
marks = {"rafi": 72, "ahmed": 45}
for name in marks:
print(name)
print("---")
for name, mark in marks.items():
print(name, mark)
print("---")
print(list(marks.keys()))
print(list(marks.values()))rafi
ahmed
---
rafi 72
ahmed 45
---
['rafi', 'ahmed']
[72, 45]La prima merita attenzione: for name in marks ti dà le chiavi, non i valori. La prima volta sorprende tutti.
Quando vuoi entrambe, .items() restituisce una tupla a ogni giro — e name, mark ne fa l'unpacking. È qui che l'unpacking del capitolo precedente dimostra il suo valore.
Per calcolare sui valori, .values():
marks = {"rafi": 72, "ahmed": 45, "bilal": 90}
print(sum(marks.values()))
print(max(marks.values()))
print(round(sum(marks.values()) / len(marks), 2))207
90
69.0Cosa può essere una chiave
Per essere una chiave, una cosa deve essere immutabile. Testo, numeri e tuple funzionano tutti. Una lista no:
d = {}
d[["a", "b"]] = 1TypeError: unhashable type: 'list'Quella parola "unhashable" merita di essere analizzata. Un dizionario trova le cose così velocemente perché calcola un numero da ogni chiave e usa quel numero per andare dritto al posto giusto. Se la chiave potesse cambiare dopo, quel numero sarebbe sbagliato e la voce andrebbe persa. Quindi Python rifiuta di lasciare che qualcosa di modificabile sia una chiave.
Una tupla non può cambiare, il che la rende un'ottima chiave:
grid = {}
grid[(0, 0)] = "start"
grid[(1, 2)] = "tree"
print(grid)
print(grid[(1, 2)]){(0, 0): 'start', (1, 2): 'tree'}
treeIl capitolo precedente chiamava l'immutabilità di una tupla un vantaggio. Questa è la prima prova pratica.
Il pattern più utile che ci sia — il conteggio
words = ["pen", "bag", "pen", "ink", "pen"]
counts = {}
for word in words:
counts[word] = counts.get(word, 0) + 1
print(counts){'pen': 3, 'bag': 1, 'ink': 1}Leggi la riga dall'interno: counts.get(word, 0) dice "dammi finora il conteggio di questa parola, e zero se non ne ha ancora", poi si aggiunge uno e il risultato viene rimesso al suo posto.
Il default di .get() fa qui tutto il lavoro — senza di esso, ogni nuova parola solleverebbe un KeyError e avresti bisogno di if word in counts: e di due rami. Scriverai questo pattern di una riga molte volte.
Un esempio completo
marks.py:
# A dictionary keeps a name attached to its value
marks = {"rafi": 72, "ahmed": 45, "bilal": 90, "dia": 88}
print("Everyone:")
for name, mark in marks.items():
status = "pass" if mark >= 40 else "fail"
print(f" {name:<8} {mark:>3} {status}")
print()
print("Count :", len(marks))
print("Total :", sum(marks.values()))
print("Average :", round(sum(marks.values()) / len(marks), 2))
print("Highest :", max(marks.values()))
best = max(marks, key=marks.get)
print("Best :", best, marks[best])
print("Has dia :", "dia" in marks)
print("Missing :", marks.get("nadia", "not recorded"))
marks["dia"] = 91
marks["nadia"] = 60
del marks["ahmed"]
print()
print("After edits:", marks)Everyone:
rafi 72 pass
ahmed 45 pass
bilal 90 pass
dia 88 pass
Count : 4
Total : 295
Average : 73.75
Highest : 90
Best : bilal 90
Has dia : True
Missing : not recorded
After edits: {'rafi': 72, 'bilal': 90, 'dia': 91, 'nadia': 60}Tre cose da notare.
status = "pass" if mark >= 40 else "fail" è un modo di scrivere una condizione in una riga, da leggere dal centro verso l'esterno: ""pass" se mark >= 40, altrimenti "fail"". L'if/else su quattro righe del capitolo nove qui sceglie un singolo valore, quindi una riga basta. Scrivi un if completo per una vera decisione — questa forma serve solo per scegliere uno di due valori.
max(marks, key=marks.get) è il key= del capitolo tredici che fa un nuovo lavoro. (Qui key= è il nome di un argomento di max, non una chiave del dizionario.) max(marks) darebbe la chiave più grande, cioè l'ultimo nome in ordine alfabetico. key=marks.get dice di confrontare ogni chiave per il suo valore, e così si ottiene il nome col voto più alto. Nota che ottenere il voto più alto è facile (max(marks.values())); scoprire di chi è quello più alto richiede questo trucco.
L'ordine sull'ultima riga. dia esisteva già, quindi il suo valore è cambiato ma la sua posizione no. nadia è nuova, quindi è stata aggiunta alla fine. E ahmed non c'è più. Un dizionario ricorda l'ordine di inserimento, e sostituire un valore non lo disturba.
Quando qualcosa va storto
KeyError: 'dia' Non esiste una chiave con quel nome. Controlla ortografia e maiuscole/minuscole — "Rafi" e "rafi" sono due chiavi diverse. Le chiavi lette da un file spesso hanno uno spazio finale; print(list(d.keys())) mette le virgolette attorno a ciascuna, così si vede. Se il fatto che manchi è normale, usa .get().
TypeError: unhashable type: 'list' Un tentativo di usare una lista come chiave. Usa una tupla — tuple(my_list) ne crea una.
for x in my_dict non mi dà i valori E non dovrebbe — dà le chiavi. Per i valori usa .values(), e per entrambi .items().
Due chiavi erano uguali e un record è sparito Una chiave esiste una sola volta, e quella successiva ha rimpiazzato in silenzio quella precedente. Per tenere più record sotto un unico nome, rendi il valore una lista — l'argomento del capitolo diciassette.
KeyError: 0 Il codice ha scritto my_dict[0], chiedendo il primo elemento come se fosse una lista. Un dizionario non ha posizioni; 0 viene cercato come chiave, e quando una chiave del genere non c'è ottieni KeyError: 0. Se ti serve davvero la prima coppia, scrivi list(my_dict.items())[0].
.sort() non funziona su un dizionario Corretto — i dizionari non hanno .sort(). sorted(d.items()) restituisce una lista ordinata, che di solito è ciò che volevi.
Passo 4 di 6 — Prevedi
Verifica cosa hai capito
Un ciclo gira direttamente su un dizionario. Cosa viene stampato?
marks = {"rafi": 72, "ahmed": 45}
for name in marks:
print(name)- Arafi 72 ahmed 45
- Brafi ahmed
- C72 45
- D('rafi', 72) ('ahmed', 45)
Viene chiesta una chiave che non c'è. Cosa succede, e qual è l'alternativa sicura?
marks = {"rafi": 72}
print(marks["dia"])- AStampa `None`
- BStampa una riga vuota
- CUn `KeyError` — l'alternativa sicura è `marks.get("dia")`, che dà `None`
- DUn `IndexError`
Il pattern di conteggio. Cosa viene stampato?
words = ["pen", "bag", "pen"]
counts = {}
for word in words:
counts[word] = counts.get(word, 0) + 1
print(counts)- AUn `KeyError`
- B{'pen': 2, 'bag': 1}
- C{'pen': 1, 'bag': 1}
- D{'pen': 3, 'bag': 1}
Per rispondere serve un account
Accedi per verificare le tue risposte
Le domande sono qui sopra, e ragionarci a mente è la parte che conta. Accedi per vedere le risposte, le spiegazioni e i suggerimenti a tre livelli.
Tocca a te
Scrivi un file chiamato stock.py che contiene un dizionario di almeno sei prodotti — il nome del prodotto come chiave, il prezzo come valore.
Poi:
- Stampa ogni prodotto e il suo prezzo in colonne allineate
- Stampa il totale, la media, il prezzo più alto e il più basso
- Trova il nome del prodotto più caro, non solo il prezzo
- Aggiungi un prodotto, cambia un prezzo, elimina un prodotto, poi stampa di nuovo tutto
- Usa
.get()per chiedere un prodotto che non esiste, con un messaggio come valore predefinito
Poi scrivi un programma di conteggio: metti diverse parole in una lista, alcune ripetute, e conta quante volte ognuna appare usando il pattern counts.get(word, 0) + 1.
Infine rompilo di proposito: chiedi una chiave che manca con le parentesi quadre, e prova a usare una lista come chiave. Torneranno indietro due errori diversi — spiegati quale è quale, e perché.
Passo 6 di 6
Sfida — il quiz del capitolo
Dieci domande dalla più facile alla più difficile. Le ultime sono difficili di proposito.
Accedi per fare il quiz