Dizionari 1 - Introduzione

Scarica zip esercizi

Naviga file online

I dizionari sono dei contenitori mutabili che ci consentono di associare velocemente voci dette chiavi a dei valori

  • Le chiavi sono immutabili, non hanno ordine e non vi possono essere duplicati

  • I valori possono essere duplicati

Data una chiave, possiamo reperire velocemente il valore corrispondente.

Riferimenti:

Che fare

  • scompatta lo zip in una cartella, dovresti ottenere qualcosa del genere:

dictionaries
    dictionaries1.ipynb
    dictionaries1-sol.ipynb
    dictionaries2.ipynb
    dictionaries2-sol.ipynb
    dictionaries3.ipynb
    dictionaries3-sol.ipynb
    dictionaries4.ipynb
    dictionaries4-sol.ipynb
    dictionaries5.ipynb
    dictionaries5-sol.ipynb
    jupman.py

ATTENZIONE: Per essere visualizzato correttamente, il file del notebook DEVE essere nella cartella szippata.

  • apri il Jupyter Notebook da quella cartella. Due cose dovrebbero aprirsi, prima una console e poi un browser. Il browser dovrebbe mostrare una lista di file: naviga la lista e apri il notebook dictionaries1.ipynb

  • Prosegui leggendo il file degli esercizi, ogni tanto al suo interno troverai delle scritte ESERCIZIO, che ti chiederanno di scrivere dei comandi Python nelle celle successive. Gli esercizi sono graduati per difficoltà, da una stellina ✪ a quattro ✪✪✪✪

Scorciatoie da tastiera:

  • Per eseguire il codice Python dentro una cella di Jupyter, premi Control+Invio

  • Per eseguire il codice Python dentro una cella di Jupyter E selezionare la cella seguente, premi Shift+Invio

  • Per eseguire il codice Python dentro una cella di Jupyter E creare una nuova cella subito dopo, premi Alt+Invio

  • Se per caso il Notebook sembra inchiodato, prova a selezionare Kernel -> Restart

Creare un dizionario

Nella vita di tutti i giorni quando pensiamo ad un dizionario tipicamente ci riferiamo ad un libro che data una voce (per es. 'sedia'), ci permette rapidamente di trovare la relativa descrizione (es. 'un mobile per sedersi').

In Python abbiamo una struttura dati chiamata dict che ci consente di rappresentare comodamente i dizionari.

Riprendendo l’esempio di prima, potremmo creare un dict con diverse voci così:

[2]:
{'sedia':'un mobile per sedersi',
 'armadio':'un mobile a ripiani',
 'lampadario': 'un apparecchio di illuminazione'
}

[2]:
{'armadio': 'un mobile a ripiani',
 'lampadario': 'un apparecchio di illuminazione',
 'sedia': 'un mobile per sedersi'}

Chiariamo un momento i nomi:

I dizionari sono dei contenitori mutabili che ci consentono di associare velocemente voci dette chiavi a dei valori.

La definizione ci dice che le voci in Python vengono chiamate chiavi (nell esempio 'sedia', 'armadio' etc), mentre quelle che nell’esempio sono descrizioni ('un mobile per sedersi') in Python le chiamiamo valori.

Quando creaiamo un dizionario, scriviamo prima una graffa {, poi la facciamo seguire da una serie di coppie chiave : valore, ciascuna seguita da una virgola , (tranne l’ultima, in cui la virgola è opzionale). Alla fine chiudiamo con una graffa }.

Mettere spazi o ritorni a capo all’interno è opzionale. Quindi possiamo scrivere anche così:

[3]:
{
 'sedia'     : 'un mobile per sedersi',
 'armadio'   : 'un mobile a ripiani',
 'lampadario': 'un apparecchio di illuminazione'
}
[3]:
{'armadio': 'un mobile a ripiani',
 'lampadario': 'un apparecchio di illuminazione',
 'sedia': 'un mobile per sedersi'}

O anche tutto su una riga:

[4]:
{'sedia':'un mobile per sedersi','armadio':'un mobile a ripiani','lampadario':'un apparecchio di illuminazione'}
[4]:
{'armadio': 'un mobile a ripiani',
 'lampadario': 'un apparecchio di illuminazione',
 'sedia': 'un mobile per sedersi'}

Nota che se usiamo parole brevi Python probabilmente stamperà il dizionario comunque su una riga:

[5]:
{'barca': 'remo',
 'auto': 'ruota',
 'aereo': 'ala'}
[5]:
{'aereo': 'ala', 'auto': 'ruota', 'barca': 'remo'}

Mettere una virgola anche dopo l’ultima coppia non provoca errori:

[6]:
{
 'barca': 'remo',
 'auto': 'ruota',
 'aereo': 'ala',  # nota virgola 'extra'
}
[6]:
{'aereo': 'ala', 'auto': 'ruota', 'barca': 'remo'}

Vediamo come viene rappresentato un dizionario in Python Tutor - per agevolarci, lo assegnamo alla variabile diz

[7]:
# AFFINCHE' PYTHON TUTOR FUNZIONI, RICORDATI DI ESEGUIRE QUESTA CELLA con Shift+Invio
#   (basta eseguirla una volta sola, la trovi anche all'inizio di ogni foglio)

import jupman
[8]:

diz = {
 'sedia'     : 'un mobile per sedersi',
 'armadio'   : 'un mobile a ripiani',
 'lampadario': 'un apparecchio di illuminazione'
}
print(diz)

jupman.pytut()
{'armadio': 'un mobile a ripiani', 'lampadario': 'un apparecchio di illuminazione', 'sedia': 'un mobile per sedersi'}
[8]:

Notiamo che una volta eseguito, appare una freccia da diz che punta ad una regione di memoria arancione/gialla. Le chiavi hanno sfondo arancione, mentre i corrispondenti valori hanno sfondo giallo. Dalle frecce e colori, si può intuire che quando si parla di assegnazioni di variabili, i dizionari si comportano come altre strutture dati mutabili, come le liste e gli insiemi.

DOMANDA: Guarda il codice seguente, e prova ad immaginare cosa succederà durante l’esecuzione - alla fine, come sarà organizzata la memoria? Cosa sarà stampato? Dove andranno le frecce?

[9]:

da = {
 'sedia'     : 'un mobile per sedersi',
 'armadio'   : 'un mobile a ripiani',
 'lampadario': 'un apparecchio di illuminazione'
}

db = {
 'barca': 'remo',
 'auto': 'ruota',
 'aereo': 'ala'
}
dc = db
db = da
da = dc
dc = db
#print(da)
#print(db)
#print(dc)

jupman.pytut()
[9]:

Le chiavi

Cerchiamo di capire meglio quali chiavi possiamo mettere riguardando la definizione:

I dizionari sono dei contenitori mutabili che ci consentono di associare velocemente voci dette chiavi a dei valori

  • Le chiavi sono immutabili, non hanno ordine e non vi possono essere duplicati

  • I valori possono essere duplicati

DOMANDA: guarda bene le parole in grassetto - ti viene in mente una struttura dati già vista che ha quelle caratteristiche?

Mostra risposta

Le chiavi sono immutabili

DOMANDA: La definizione non impone di usare per forza stringhe come chiavi, possiamo usare anche altri tipi. Ma possiamo usare tutti quelli che vogliamo?

Per ciascuno degli esempi seguenti, prova a dire se il dizionario si può creare o se otterremo un errore (quale?). Controlla come sono rappresentati in Python Tutor.

  1. interi

    {
      4 : 'gatti',
      3 : 'cani'
    }
    
  2. float

    {
      4.0 : 'gatti',
      3.0 : 'cani'
    }
    
  3. stringhe

    {
      'a' : 'gatti',
      'b' : 'cani'
    }
    
  4. liste

    {
        [1,2] : 'zam',
        [3,4] : 'zum'
    }
    
  5. tuple

    {
        (1,2) : 'zam',
        (4,3) : 'zum'
    }
    
  6. insiemi

    {
        {1,2} : 'zam',
        {3,4} : 'zum'
    }
    
  7. altri dizionari (guarda la prima parte della definizione !)

    {
        {'a':'x','b':'y'} : 'zam',
        {'c':'w','d':'z'} : 'zum'
    }
    
Mostra risposta

Le chiavi non hanno ordine

In un dizionario che possiamo trovare nella vita reale, le voci sono sempre ordinate secondo qualche criterio, (tipicamente in ordine alfabetico)

Con Python invece dobbiamo considerare un’importante differenza:

  • Le chiavi sono immutabili, non hanno ordine e non vi possono essere duplicati

Quando diciamo che una collezione ‘non ha ordine’, significa che l’ordine degli elementi che vediamo quando li inseriamo o stampiamo non ha alcuna importanza nel determinare se una collezione è uguale ad un’altra. Nel caso dei dizionari significa che cambiando l’ordine in cui sono specificate le coppie, otteniamo dizionari che Python considera uguali.

Per esempio, i seguenti dizionari sono da considerarsi tutti uguali:

[10]:
{
 'navi' :'porto',
 'aerei': 'aereoporto',
 'treni': 'stazione'
}
[10]:
{'aerei': 'aereoporto', 'navi': 'porto', 'treni': 'stazione'}
[11]:
{
 'aerei': 'aereoporto',
 'navi' :'porto',
 'treni': 'stazione'
}
[11]:
{'aerei': 'aereoporto', 'navi': 'porto', 'treni': 'stazione'}
[12]:
{
 'treni': 'stazione',
 'navi' : 'porto',
 'aerei': 'aereoporto',
}
[12]:
{'aerei': 'aereoporto', 'navi': 'porto', 'treni': 'stazione'}

Stampare un dizionario: avrai notato che Jupyter stampa sempre le chiavi nello stesso ordine alfabetico. Questa è una cortesia che ci fa Jupyter, ma non lasciarti ingannare! Se proviamo una stampa nativa con print otterremo un risultato diverso!

[13]:
print({
 'navi' :'porto',
 'aerei': 'aereoporto',
 'treni': 'stazione'
})
{'navi': 'porto', 'aerei': 'aereoporto', 'treni': 'stazione'}

Duplicati delle chiavi

Le chiavi sono immutabili, non hanno ordine e non vi possono essere duplicati

Possiamo chiederci come Python gestisca i duplicati nelle chiavi. Proviamo di proposito a creare una coppia duplicata:

[14]:
{
 'sedia'     : 'un mobile per sedersi',
 'sedia'     : 'un mobile per sedersi',
 'lampadario': 'un apparecchio di illuminazione'
}

[14]:
{'lampadario': 'un apparecchio di illuminazione',
 'sedia': 'un mobile per sedersi'}

Notiamo che Python non si è lamentato e ha silenziosamente scartato il duplicato. E se provassimo ad inserire una coppia con stessa chiave ma valore diverso?

[15]:
{
 'sedia'     : 'un mobile per sedersi',
 'sedia'     : 'appoggio con schienale',
 'lampadario': 'un apparecchio di illuminazione'
}

[15]:
{'lampadario': 'un apparecchio di illuminazione',
 'sedia': 'appoggio con schienale'}

Notiamo che Python ha mantenuto solo l’ultima coppia.

I valori

Riguardiamo la definizione:

I dizionari sono dei contenitori mutabili che ci consentono di associare velocemente voci dette chiavi a dei valori.

  • Le chiavi sono immutabili, non hanno ordine e non vi possono essere duplicati

  • I valori possono essere duplicati

Pare che per i valori vi siano meno vincoli rispetto alle chiavi

DOMANDA: Per ciascuno degli esempi seguenti, prova a dire se il dizionario si può creare o se otterremo un errore (quale?). Controlla come sono rappresentati in Python Tutor.

  1. interi

    {
        'a':3,
        'b':4
    }
    
  2. interi duplicati

    {
        'a':3,
        'b':3
    }
    
  3. float

    {
        'a':3.0,
        'b':4.0
    }
    
  4. stringhe

    {
        'a' : 'ghiaccio',
        'b' : 'fuoco'
    }
    
  5. liste

    {
        'a' : ['t','w'],
        'b' : ['x'],
        'c' : ['y','z','k']
    }
    
  6. liste duplicate

    {
        'a' : ['x','y','z'],
        'b' : ['x','y','z']
    }
    
  7. liste contenenti duplicati

    {
        'a' : ['x','y','y'],
        'b' : ['z','y','z']
    }
    
  8. tuple

    {
        'a': (6,9,7),
        'b': (8,1,7,4)
    }
    
  9. insiemi

    {
        'a' : {6,5,6},
        'b' : {2,4,1,5}
    }
    
  10. dizionari

    {
        'a': {
                'x':3,
                'y':9
             },
        'b': {
                'x':3,
                'y':9,
                'z':10
             },
    }
    
Mostra risposta

Dizionario vuoto

Possiamo creare un dizionario vuoto scrivendo {}:

ATTENZIONE: DA NON CONFONDERSI CON L’INSIEME VUOTO !!

[16]:
{}
[16]:
{}
[17]:
type({})
[17]:
dict

Un dizionario è una collezione, e come già visto (con liste, tuple e insiemi), possiamo creare un dizionario vuoto scrivendo il suo tipo dict seguito da parentesi tonde:

[18]:
dict()
[18]:
{}

Vediamo come viene rappresentato in Python Tutor:

[19]:

diz = dict()

jupman.pytut()
[19]:

Chiavi e valori eterogenei

Finora abbiamo sempre usato chiavi tutte dello stesso tipo e valori tutti dello stesso tipo, ma non è obbligatorio. Basta che i tipi delle chiavi siano immutabili:

[20]:
{
    "a": 3,
    "b": ["una", "lista"],
     7 : ("questa","è","una","tupla")
}
[20]:
{7: ('questa', 'è', 'una', 'tupla'), 'a': 3, 'b': ['una', 'lista']}

NOTA: Sebbene mischiare tipi sia possibile, è sconsigliabile!

Buttare nel dizionario tipi diversi spesso porta sfortuna, nel senso che aumenta la probabilità di incorrere in bug.

DOMANDA: Guarda le seguenti espressioni, e per ciascuna cerca di indovinare quale risultato producono (o se danno errore):

  1. {'a':'b'       ,
     'c':'d'
    
     }
    
  2. {'a b':'c',
     'c d':'e f'}
    
  3. {'a' = 'c',
     'b' = 'd'}
    
  4. {'a':'b':
     'c':'d'}
    
  5. {
        "1":[2,3],
        "2,3":1,
    }
    
  6. type({'a:b,c:d'})
    
  7. {'a':'b';
     'c':'d'}
    
  8. {'a:b',
     'c:d'}
    
  9. {5,2:
     4,5}
    
  10. {1:2,
     1:3}
    
  11. {2:1,
     3:1}
    
  12. {'a':'b',
     'c':'d',}
    
  13. type({'a','b',
          'c','d'})
    
  14. {'a':'b',
     'c':'d',
     'e','f'}
    
  15. {{}: 2}
    
  16. {(1,2):[3,4]}
    
  17. {[1,2]:(3,4)}
    
  18. {'[1,2]':(3,4)}
    
  19. {{1,2}:(3,4)}
    
  20. {len({1,2}):(3,4)}
    
  21. {5:{'a':'b'}}
    
  22. {"a":{1:2}}
    
  23. {"a":{[1]:2}}
    
  24. {"a":{1:[2]}}
    
  25. {["a":{1:[2]}]}
    
  26. set([{2:4}])
    

Dizionario da sequenza di coppie

Possiamo ottenere un dizionario specificando come parametro della funzione dict una sequenza di coppie chiave/valore, per esempio potremmo passare una lista di tuple:

[21]:
dict(  [
         ('farina',500),
         ('uova',2),
         ('zucchero',200),
       ])
[21]:
{'farina': 500, 'uova': 2, 'zucchero': 200}

Possiamo usare anche altre sequenze, l’importante è che le sottosequenze siano tutte da due elementi. Qua per esempio abbiamo una tupla di liste:

[22]:
dict(  (
         ['farina',500],
         ['uova',2],
         ['zucchero',200],
       ))
[22]:
{'farina': 500, 'uova': 2, 'zucchero': 200}

Se una sottosequenza ha un numero di elementi diverso da due, otteniamo questo errore:

>>> dict(  (
         ['farina',500],
         ['uova','marce', 3,],
         ['zucchero',200],
       ))

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-88-563d301b4aef> in <module>
      2          ['farina',500],
      3          ['uova','marce', 3,],
----> 4          ['zucchero',200],
      5        ))

ValueError: dictionary update sequence element #1 has length 3; 2 is required

DOMANDA: Compara i seguenti codici. Fanno la stessa cosa? Se sì, quale preferiresti?

dict( {
        ('a',5),
        ('b',8),
        ('c',3)
      } )
dict( (
        {'a',5},
        {'b',8},
        {'c',3}
      )
    )
Mostra risposta

DOMANDA: Guarda i seguenti frammenti di codice, e per ciascuno cerca di indovinare quale risultato produce (o se da errore):

  1. dict('abcd')
    
  2. dict(['ab','cd'])
    
  3. dict(['a1','c2'])
    
  4. dict([])
    
  5. dict(())
    
  6. dict(('  ',))   # nasty
    

Dizionario da parametri con nome

Come ulteriore metodo di creazione, possiamo specificare le chiavi come se fossero parametri con nome:

[23]:
dict(a=5,b=6)
[23]:
{'a': 5, 'b': 6}

ATTENZIONE: in questo caso le chiavi sono soggette alle stesse restrittive regole dei nomi di parametri di funzione!

Per esempio, usando le graffe questo dizionario è perfettamente legittimo:

[24]:
{'a b' : 2,
 'c d' : 6}
[24]:
{'a b': 2, 'c d': 6}

Ma se proviamo a crearlo usando a b come argomento di dict, avremo dei problemi:

>>> dict(a b=2, c d=6)

  File "<ipython-input-97-444f8661585a>", line 1
    dict(a b=2, c d=6)
           ^
SyntaxError: invalid syntax

Avremo dei problemi anche usando stringhe:

>>> dict('a b'=2,'c d'=6)

  File "<ipython-input-98-45aafbb56e81>", line 1
    dict('a b'=2,'c d'=6)
        ^
SyntaxError: keyword can't be an expression

E attenzione a furbate tipo usare variabili, non otterremo il risultato desiderato:

[25]:
ka = 'a b'
kc = 'c d'

dict(ka=2,kc=6)
[25]:
{'ka': 2, 'kc': 6}

DOMANDA: Guarda i seguenti frammenti di codice, e per ciascuno cerca di indovinare quale risultato produce (o se da errore):

  1. dict(3=5,2=8)
    
  2. dict('costi'=9,'ricavi'=15)
    
  3. dict(_costi=9,_ricavi=15)
    
  4. dict(33trentini=5)
    
  5. dict(trentini33=5)
    
  6. dict(trentini_33=5)
    
  7. dict(trentini-33=5)
    
  8. dict(costi=1=2,ricavi=3=3)
    
  9. dict(costi=1==2,ricavi=3==3)
    
  10. v1 = 6
    v2 = 8
    dict(k1=v1,k2=v2)
    

Copiare un dizionario

Ci sono due modi di copiare un dizionario, si può fare una copia superficiale (shallow copy) oppure una copia in profondità (deep copy).

Copia superficiale

E’ possibile creare una copia superficiale (shallow copy) di un dizionario passando alla funzione dict un’altro dizionario:

[26]:
da = {'x':3,
      'y':5,
      'z':1}
[27]:
db = dict(da)
[28]:
print(da)
{'x': 3, 'y': 5, 'z': 1}
[29]:
print(db)
{'x': 3, 'y': 5, 'z': 1}

In Python Tutor vedremo apparire due regioni di memoria separate:

[30]:
da = {'x':3,
      'y':5,
      'z':1}
db = dict(da)

jupman.pytut()
[30]:

DOMANDA: possiamo scrivere così? Rispetto all’esempio precedente, otterremo risultati diversi?

[31]:
da = {'x':3,
      'y':5,
      'z':1}
db = dict(dict(da))

jupman.pytut()
[31]:
Mostra risposta

Valori mutabili: Nell’esempio abbiamo usato valori interi, che sono immutabili. E se provassimo dei valori mutabili come delle liste, che succederebbe?

[32]:
da = {'x':['a','b','c'],
      'y':['d'],
      'z':['e','f']}
db = dict(da)

jupman.pytut()
[32]:

Se provi ad eseguire Python Tutor, vedrai un’esplosione di frecce che vanno dal nuovo dizionario db ai valori di da (che sono liste). Niente paura ! Nel prossimo foglio spiegheremo meglio il significato, per il momento tieni nota che con la copia superficiale di valori mutabili il nuovo dizionario avrà in comune regioni di memoria con il dizionario originale.

Copia in profondità

Quando ci sono regioni di memoria mutabili condivise come nel caso qua sopra, è più probabile introdurre bug.

Per aver regioni di memoria completamente separate, possiamo usare la copia in profondità (deep copy).

Per usarla, dobbiamo prima dire a Python che intendiamo usare funzioni presenti nel modulo copy, poi potremo usare la funzione deepcopy, a cui passeremo il dizionario da copiare:

[33]:
import copy

da = {'x':['a','b','c'],
      'y':['d'],
      'z':['e','f']}
db = copy.deepcopy(da)

jupman.pytut()
[33]:

Se esegui il codice in Python Tutor, noterai come partendo con la freccia da db, finiremo in una regione di memoria arancione/gialla completamente nuova che non condivide nulla con la regione di memoria puntata da da.

DOMANDA: Guarda il codice seguente - dopo la sua esecuzione, vedrai frecce che da db arrivano ad elementi di da?

[34]:
da = {'x': {1,2,3},
      'y': {4,5}}
db = dict(da)
jupman.pytut()
[34]:
Mostra risposta

Prosegui

Prosegui con Dizionari 2

[ ]: