Salve a tutti ragazzi! Oggi vedremo una struttura del Python molto interessante, il dizionario. Come probabilmente avrò già detto in qualche lezione precedente, tutte le strutture dati che stiamo presentando, sono fondamentali per padroneggiare il Python almeno ad un livello base. Quindi massima attenzione 😉
Dizionari
I tipi di dati composti che abbiamo visto finora (stringhe, liste e tuple) usano gli interi come indici. Qualsiasi tentativo di usare altri tipi di dati produce un errore.
I dizionari sono simili agli altri tipi composti ma si differenziano per il fatto di poter usare qualsiasi tipo di dato immutabile come indice. Se desideriamo creare un dizionario per la traduzione di parole dall’inglese all’italiano è utile poter usare la parola inglese come indice di ricerca della corrispondente italiana. Gli indici usati sono in questo caso delle stringhe.
Un modo per creare un dizionario è partire con un dizionario vuoto e aggiungere via via gli elementi. Il dizionario vuoto è indicato da {}:
>>> Eng2Ita = {} >>> Eng2Ita[’one’] = ’uno’ >>> Eng2Ita[’two’] = ’due’
La prima assegnazione crea un dizionario chiamato Eng2Ita; le altre istruzioni aggiungono elementi al dizionario. Possiamo stampare il valore del dizionario nel solito modo:
>>> print Eng2Ita {’one’: ’uno’, ’two’: ’due’}
Gli elementi di un dizionario appaiono in una sequenza separata da virgole. Ogni voce contiene un indice ed il corrispondente valore separati da due punti. In un dizionario gli indici sono chiamati chiavi e un elemento è detto coppia chiave-valore.
Un altro modo di creare un dizionario è quello di fornire direttamente una serie di coppie chiave-valore:
>>> Eng2Ita = {’one’: ’uno’, ’two’: ’due’, ’three’: ’tre’}
Se stampiamo ancora una volta il valore di Eng2Ita abbiamo una sorpresa:
>>> print Eng2Ita {’one’: ’uno’, ’three’: ’tre’, ’two’: ’due’}
Le coppie chiave-valore non sono in ordine! Per fortuna non c’è ragione di conservare l’ordine di inserimento dato che il dizionario non fa uso di indici numerici. Per cercare un valore usiamo infatti una chiave:
>>> print Eng2Ita[’two’] ’due’
La chiave ’two’ produce correttamente ’due’ anche se appare in terza posizione nella stampa del dizionario.
Operazioni sui dizionari
L’istruzione del rimuove una coppia chiave-valore da un dizionario. Vediamo di fare un esempio pratico creando un dizionario che contiene il nome di vari tipi di frutta (la chiave) ed il numero di frutti corrispondenti in magazzino (il valore):
>>> Magazzino = {’mele’: 430, ’banane’: 312, ’arance’: 525, ’pere’: 217} >>> print Magazzino {’banane’: 312, ’arance’: 525, ’pere’: 217, ’mele’: 430}
Dovessimo togliere la scorta di pere dal magazzino possiamo direttamente ri- muovere la voce dal dizionario:
>>> del Magazzino[’pere’] >>> print Magazzino {’banane’: 312, ’arance’: 525, ’mele’: 430}
o se intendiamo solo cambiare il numero di pere senza rimuoverne la voce dal dizionario possiamo cambiare il valore associato:
>>> Magazzino[’pere’] = 0 >>> print Magazzino {’banane’: 312, ’arance’: 525, ’pere’: 0, ’mele’: 430}
La funzione len opera anche sui dizionari ritornando il numero di coppie chiave- valore:
>>> len(Magazzino) 4
Metodi dei dizionari
Un metodo è simile ad una funzione, visto che prende parametri e ritorna valori, ma la sintassi di chiamata è diversa. Il metodo keys prende un dizionario e ritorna la lista delle sue chiavi: invece di invocarlo con la sintassi delle funzioni keys(Eng2Ita) usiamo la sintassi dei metodi Eng2Ita.keys():
>>> Eng2Ita.keys() [’one’, ’three’, ’two’]
Questa forma di notazione punto specifica il nome della funzione keys ed il nome dell’oggetto cui applicare la funzione Eng2Ita. Le parentesi vuote indicano che questo metodo non prende parametri.
Una chiamata ad un metodo è detta invocazione; in questo caso diciamo che stiamo invocando keys sull’oggetto Eng2Ita.
Il metodo values funziona in modo simile: ritorna la lista dei valori in un dizionario:
>>> Eng2Ita.values() [’uno’, ’tre’, ’due’]
Il metodo items ritorna entrambi nella forma di una lista di tuple, una per ogni coppia chiave-valore:
>>> Eng2Ita.items() [(’one’,’uno’), (’three’, ’tre’), (’two’, ’due’)]
La sintassi fornisce utili informazioni sul tipo ottenuto invocando items: le parentesi quadrate indicano che si tratta di una lista; le parentesi tonde che gli elementi della lista sono tuple.
Se un metodo prende un argomento usa la stessa sintassi delle chiamate di funzioni. Il metodo has key prende come argomento una chiave e ritorna vero (1) se la chiave è presente nel dizionario:
>>> Eng2Ita.has_key(’one’) 1 >>> End2Ita.has_key(’deux’) 0
Se provi a invocare un metodo senza specificare l’oggetto cui si fa riferimento ottieni un errore:
>>> has_key(’one’) NameError: has_key
Purtroppo il messaggio d’errore a volte, come in questo caso, non è del tutto chiaro: Python cerca di dirci che la funzione has key non esiste, dato che con questa sintassi abbiamo chiamato la funzione has key e non invocato il metodo has key dell’oggetto.
Alias e copia
Visto che i dizionari sono mutabili devi stare molto attento agli alias: quan- do due variabili si riferiscono allo stesso oggetto un cambio effettuato su una influenza immediatamente il contenuto dell’altra. Se desiderate poter modificare un dizionario e mantenere una copia dell’originale usate il metodo copy. Per fare un esempio costruiamo un dizionario Opposti che contiene coppie di parole dal significato opposto:
>>> Opposti = {’alto’: ’basso’, ’giusto’: ’sbagliato’, ’vero’: ’falso’} >>> Alias = Opposti >>> Copia = Opposti.copy()
Alias e Opposti si riferiscono allo stesso oggetto; Copia si riferisce ad una copia del dizionario nuova di zecca. Se modifichiamo Alias, Opposti viene modificato:
>>> Alias[’giusto’] = ’errato’ >>> Opposti[’giusto’] ’errato’
Opposti resta immutato se modifichiamo Copia:
>>> Copia[’giusto’] = ’errato’ >>> Opposti[’giusto’] ’sbagliato’
Matrici sparse
Per rappresentare una normale matrice possiamo usare una lista di liste. Questa è una buona scelta quando si tratta di rappresentare matrici i cui valori sono in buona parte diversi da zero, ma c’è un tipo di matrice detta “sparsa” i cui valori sono di tipo particolare. La matrice sparsa è una matrice che ha tantissimi zeri e memorizzare tutti questi zeri è inutile. I dizionari ci vengono incontro, grazie alla loro sintassi flessibile ci forniscono un metodo efficace per memorizzare e rappresentare le matrici sparse.
>>> Matrice = [ [0,0,0,1,0], [0,0,0,0,0], [0,2,0,0,0], [0,0,0,0,0], [0,0,0,3,0] ]
L’alternativa quindi in questo caso è quella di usare un dizionario, usando come chiavi tuple composte dalla coppia riga/colonna. Ecco la stessa matrice rappresentata con un dizionario:
>>> Matrice = {(0,3): 1, (2, 1): 2, (4, 3): 3}
In questo caso abbiamo solo 3 coppie chiave-valore, una per ciascun elemento diverso da zero nella matrice. Ogni chiave è una tupla ed ogni valore un intero. Per l’accesso ad un elemento della matrice possiamo usare l’operatore []:
>>> Matrice[(0,3)] 1 >>> Matrice[0,3] # questa sintassi e’ equivalente 1
Notate come la sintassi per la rappresentazione della matrice sotto forma di dizionario sia diversa da quella della lista di liste: invece di due valori indice usiamo un unico indice che è una tupla di interi. C’è un problema: se cerchiamo un elemento che è pari a zero otteniamo un errore, dato che non c’è una voce nel dizionario corrispondente alla tupla con quelle coordinate:
>>> Matrice[1,3] KeyError: (1, 3)
Il metodo get risolve il problema: >>> Matrice.get((0,3), 0)
1
Il primo argomento è la tupla-chiave, il secondo il valore che get deve ritornare
nel caso la chiave non sia presente nel dizionario:
>>> Matrice.get((1,3), 0) 0
Anche se la sintassi di get non è la più intuitiva almeno abbiamo un modo efficiente per accedere ad una matrice sparsa.
Anche per la struttura dizionario è tutto. Vi aspetto alla prossima lezione, e mi raccomando! Tanti esercizi!! 😉