Dizionari e insiemi in Python
In questa pagina 5
In questa pagina 5
Gli ADT mappa e insieme
Definizione (ADT mappa e insieme)
- Mappa (dizionario): associa a ogni chiave un valore; operazioni: inserire/aggiornare una coppia, cercare il valore di una chiave, togliere una chiave, scorrere le coppie.
- Insieme: collezione senza duplicati e senza ordine; operazioni: aggiungere, togliere, verificare l'appartenenza, unione, intersezione, differenza.
Esempio. Con voti = {"Ada": 30, "Alan": 27}, voti["Ada"] = 28 aggiorna la coppia senza duplicare la chiave; {3, 1, 4, 1, 5} vale {1, 3, 4, 5}.
Python li fornisce come tipi predefiniti dict e set (vedi Tipi di dato astrattiUn ADT è definito dalle operazioni e dal loro comportamento, non dalla rappresentazione; interfaccia e implementazione; realizzazione in Python con le classi e in C con header e tipo opaco; esempio di un ADT Frazione.Tipi di dato astratti →).
dict
Definizione (dict: operazioni principali)
voti = {"Ada": 30, "Alan": 27} vuoto = {} # dizionario vuoto (NON un insieme) d = dict(a=1, b=2) quadrati = {x: x * x for x in range(5)} # dict comprehension voti["Grace"] = 29 # inserisce voti["Ada"] = 28 # aggiorna: una chiave compare una volta sola voti["Ada"] # 28 voti["Bob"] # KeyError voti.get("Bob") # None voti.get("Bob", 0) # 0: valore di default "Ada" in voti # True: cerca tra le CHIAVI del voti["Alan"] voti.pop("Grace") # toglie e restituisce 29 len(voti)
Esempio. Con voti = {"Ada": 28, "Alan": 27}: voti.get("Bob", 0) restituisce 0, "Ada" in voti vale True e voti["Bob"] solleva KeyError.
Iterazione
for nome in voti: # chiavi
...
for nome, voto in voti.items(): # coppie
print(nome, voto)
voti.keys(), voti.values()
for nome in sorted(voti): # in ordine di chiave
...L'ordine di iterazione è quello di inserimento. Non si possono aggiungere o togliere chiavi mentre si scorre il dizionario (RuntimeError).
Chiavi
Proprietà (chiavi hashable). Le chiavi devono essere hashable (immutabili):
int,float,str,tupledi immutabili. Unalistnon può essere chiave (TypeError: unhashable type); i valori invece possono essere qualsiasi cosa.
Esempio. {(1, 2): "a"}[(1, 2)] restituisce 'a', mentre {[1]: 2} solleva TypeError.
Schemi tipici
Definizione (schemi tipici con i dizionari)
# conteggio delle frequenze freq = {} for parola in testo.split(): freq[parola] = freq.get(parola, 0) + 1 # raggruppamento per_iniziale = {} for nome in nomi: per_iniziale.setdefault(nome[0], []).append(nome) # chiave con valore massimo piu_frequente = max(freq, key=freq.get) # inversione (valori distinti) inverso = {v: k for k, v in d.items()}
Esempio. Sulle parole a b a, freq[parola] = freq.get(parola, 0) + 1 dà {'a': 2, 'b': 1}; Counter("mississippi").most_common(2) dà [('i', 4), ('s', 4)].
Con collections:
from collections import Counter, defaultdict
Counter("mississippi").most_common(2) # [('i', 4), ('s', 4)]
gruppi = defaultdict(list) # valore iniziale automatico: lista vuota
gruppi["a"].append("Ada")set
s = {3, 1, 4, 1, 5} # {1, 3, 4, 5}: i duplicati spariscono
vuoto = set() # {} sarebbe un dizionario
unici = set(lista)
s.add(9)
s.remove(3) # KeyError se manca
s.discard(3) # nessun errore se manca
4 in s # appartenenza: O(1) in mediaDefinizione (operazioni sugli insiemi)
Operazione Operatore Metodo unione a | ba.union(b)intersezione a & ba.intersection(b)differenza a - ba.difference(b)differenza simmetrica a ^ ba.symmetric_difference(b)sottoinsieme a <= ba.issubset(b)
Esempio. Con a = {1, 2, 3} e b = {3, 4}: a | b è {1, 2, 3, 4}, a & b è {3}, a - b è {1, 2} e a ^ b è {1, 2, 4}.
Gli elementi devono essere hashable; frozenset è la versione immutabile (utilizzabile come chiave o elemento di un altro insieme). Un insieme non ha indici: s[0] → TypeError.
# elementi comuni a due liste, in tempo lineare
comuni = set(a) & set(b)
# rimuovere i duplicati mantenendo l'ordine
visti, unici = set(), []
for x in lista:
if x not in visti:
visti.add(x)
unici.append(x)Come funzionano: tabelle hash
Proprietà (dict e set come tabelle hash).
dictesetsono tabelle hash: un array di celle in cui la posizione di una chiave è calcolata dahash(chiave)modulo la dimensione dell'array. Ricerca, inserimento e rimozione costano quindi in media, indipendentemente dal numero di elementi; due chiavi nella stessa cella (collisione) vengono gestite cercando un'altra cella. Quando la tabella si riempie viene ingrandita e le chiavi ridistribuite (costo ammortizzato, come l'appenddelle liste).Per questo le chiavi devono essere immutabili: se una chiave cambiasse dopo l'inserimento, il suo hash cambierebbe e non verrebbe più trovata.
listset/dictx in ...medio ordine e indici sì no (il dict mantiene l'ordine di inserimento) duplicati ammessi no (chiavi uniche)
Esempio. Con un milione di elementi x in lista può scorrerli tutti, mentre x in set(lista) costa in media ; una lista come chiave solleva TypeError.
Errori tipici
{}per creare un insieme vuoto.- Accedere con
d[k]a una chiave che può mancare: usarein,getodefaultdict. - Usare una lista come chiave: convertirla in tupla.
- Aspettarsi un ordine dagli elementi di un
set.
Versione ripasso
Gli ADT mappa e insieme
- Mappa (
dict): associa a ogni chiave un valore (inserimento, ricerca, rimozione, scorrimento). - Insieme (
set): collezione senza duplicati e senza ordine (appartenenza, unione, intersezione, differenza).
dict
voti = {"Ada": 30, "Alan": 27}
vuoto = {} # dizionario vuoto
quadrati = {x: x * x for x in range(5)} # dict comprehension
voti["Grace"] = 29 # inserisce
voti["Ada"] = 28 # aggiorna
voti["Bob"] # KeyError
voti.get("Bob", 0) # valore di default se manca
"Ada" in voti # True: cerca tra le CHIAVI
voti.pop("Grace") # toglie e restituisce- Iterazione:
for nome in voti:(chiavi),for nome, voto in voti.items():(coppie). L'ordine è quello di inserimento. Non modificare il dizionario durante lo scorrimento (RuntimeError). - Chiavi: Devono essere hashable (immutabili:
int,float,str,tuple); i valori possono essere qualsiasi cosa.
Schemi tipici
# conteggio delle frequenze
freq = {}
for parola in testo.split():
freq[parola] = freq.get(parola, 0) + 1
# Collections
from collections import Counter, defaultdict
Counter("mississippi").most_common(2) # [('i', 4), ('s', 4)]
gruppi = defaultdict(list) # valore iniziale automatico: lista vuotaset
s = {3, 1, 4, 1, 5} # {1, 3, 4, 5}: duplicati eliminati
vuoto = set() # {} crea un dizionario
s.add(9); s.remove(3); s.discard(3)
4 in s # appartenenza: O(1) in media- Operazioni insiemistiche: unione
a | b, intersezionea & b, differenzaa - b, differenza simmetricaa ^ b, sottoinsiemea <= b. frozensetè la versione immutabile (utilizzabile come chiave o elemento).
Come funzionano: tabelle hash
dict e set usano tabelle hash: la posizione è calcolata da hash(chiave) modulo la dimensione.
- Costo: Ricerca, inserimento e rimozione costano in media (vedi Complessità computazionaleCosto di un algoritmo in funzione della dimensione dell'input; caso peggiore, migliore e medio; notazione O-grande e classi di crescita; come contare i passi di cicli e ricorsioni; costo delle operazioni Python.Complessità computazionale →).
- Le chiavi devono essere immutabili altrimenti l'hash cambierebbe e non sarebbero più reperibili.
list |
set / dict |
|
|---|---|---|
x in ... |
medio | |
| ordine e indici | sì | no (dict mantiene l'ordine d'inserimento) |
| duplicati | ammessi | no |
Errori tipici
- Usare
{}per un insieme vuoto. - Usare
d[k]su una chiave potenzialmente mancante (usaregetodefaultdict). - Usare liste come chiavi (usare tuple).
- Cercare un ordine in un
set.
Esercizi su questo argomento
- Esercizio - Python di base - input, liste, dizionari e funzioni (laboratorio 1)
- Esercizio 20 · duplicati vicini con una mappa
- Esercizio 9 · codice cliente che si incrementa
- Esercizio 13 · misure da file e dizionario di misure
- Esercizio 14 · dizionario invertibile e squadra
- Esercizio 16 · oracolo di Delfi