Funzioni 2: gestione errori e testing¶

Riferimenti: SoftPython - funzioni 2

  • visualizza al meglio in
  • versione stampabile: clicca qua
  • per navigare nelle slide: premere Esc


Summer School Data Science 2023 - Modulo 1 informatica: Moodle

Docente: David Leoni david.leoni@unitn.it

Esercitatore: Luca Bosotti luca.bosotti@studenti.unitn.it

Le eccezioni 1/2¶

Come fermare l'esecuzione di tutto il programma appena riscontriamo una situazione imprevista?

Riferimenti:

  • SoftPython - Gestione errori e testing
  • Nicola Cassetta - 19: La gestione delle eccezioni

Per interrompere l'esecuzione del programma in un punto, si può inserire l'istruzione raise così:

raise Exception()

Volendo, si può anche scrivere una indicazione per i programmatori:

raise Exception("Non c'è abbastanza farina !")

Le eccezioni 2/2¶

print("PRIMA")
raise Exception("Non c'è abbastanza farina !")
print("DOPO")
PRIMA
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
/tmp/ipykernel_10578/2095967716.py in <module>
      1 print("Prima")
----> 2 raise Exception("Non c'è abbastanza farina !")
      3 print("Dopo")

Exception: Non c'è abbastanza farina !

Torta printolosa: sarà buona?¶

In [2]:
 
Out[2]:
Python Tutor visualization

Torta eccezionale: solo il meglio¶

def fai_torta_eccezionale(latte, farina):
    """ Prende ingredienti float 
        e STAMPA il procedimento

        - servono almeno 1.3 kg per il latte 
          e 1.0kg per la farina
        - se mancano ingredienti, lancia Exception
    """ 
    if latte < 1.3:
        raise Exception("Non ho abbastanza latte !")
    if farina < 1.0:
        raise Exception("Non ho abbastanza farina !")
    print("Impasto")
    print("Scaldo")
    print("Ho fatto la torta !")

fai_torta_eccezionale(5,0.2)
print("Festeggia")

Guarda in Python Tutor¶

Gestire le eccezioni¶

Se c'è una sezione di codice problematica 'che può scoppiare'...

la si può mettere in un blocco try except così:

In [3]:
try: 
    fai_torta_eccezionale(5.0,0.2)
    print("Festeggia")
except:  
    print("Non riesco a fare la torta, che ne dite se usciamo a prendere del gelato?")   

print("Fine della festa")
Non riesco a fare la torta, che ne dite se usciamo a prendere del gelato?
Fine della festa

Eccezioni particolari¶

gerarchia eccezioni

Immagine da: Mike Lam, CS 240 Data Structures and Algorithms - Fall 2014 https://w3.cs.jmu.edu/spragunr/CS240_F14/activities/exceptions/exceptions.shtml

Torta particolare 1/2¶

ValueError è una tipo di eccezione più specifico di Exception

possiamo usarla per segnalare meglio il tipo di errore che è accaduto,

così chi chiama la funzione può intercettare solo quella tipologia di errore

Guarda in Python Tutor

def fai_torta_particolare(latte, farina):
    """ Prende ingredienti come float 
        e STAMPA il procedimento

        - servono almeno 1.3 kg per il latte 
          e 1.0kg per la farina
        - se mancano ingredienti, lancia ValueError
    """     
    if latte < 1.3:
        raise ValueError("Non ho abbastanza latte !")    
    if farina < 1.0:
        raise ValueError("Non ho abbastanza farina !")
    print("Impasto")
    print("Scaldo")
    print("Ho fatto la torta !")

try: 
    fai_torta_particolare(5,0.2)
    print("Festeggia")
except ValueError:     
    print("Mancano ingredienti!")   
    print("Proviamo a chiedere ai vicini!")
    print("Ci hanno dato ingredienti, riproviamo !")    
    fai_torta_particolare(6,4) 
    print("Festeggia")   
print("Fine programma")

Torta particolare 2/2¶

Se aggiungiamo la cattura di Exception alla fine, riusciamo a gestire qualunque errore potrebbe accadere oltre a quelli già catturati precedentemente

Guarda la chiamata di funzione. Noti qualcosa di strano?

Prova in Python Tutor

def fai_torta_particolare(latte, farina):
    """ Prende ingredienti float 
        e STAMPA il procedimento
        - servono almeno 1.3 kg per il latte 
          e 1.0kg per la farina
        - se mancano ingredienti, lancia ValueError
    """     
    if latte < 1.3:
        raise ValueError("Non ho abbastanza latte !")    
    if farina < 1.0:
        raise ValueError("Non ho abbastanza farina !")
    print("Impasto")
    print("Scaldo")
    print("Ho fatto la torta !")

try: 
    fai_torta_particolare("rape","cavoli") # ???
    print("Festeggia")
except ValueError:     
    print("Mancano ingredienti!")   
    print("Proviamo a chiedere ai vicini!")
    print("Ci hanno dato ingredienti, riproviamo !")    
    fai_torta_particolare(6,4) 
    print("Festeggia")
# gestisce tutte le altre eccezioni
except Exception as eccezione:  
    print("Ragazzi, è successo un problema grave:",
          repr(eccezione))    
print("Fine programma")

assert¶

Per testare il codice, si può usare il comando assert

assert CONDIZIONE

verifica che CONDIZIONE sia vera

assert True¶

In [6]:
print("PRIMA")
assert True
print("DOPO")
PRIMA
DOPO

non fa assolutamente niente!

assert False¶

print("PRIMA")
assert False
print("DOPO")
PRIMA
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-7-a871fdc9ebee> in <module>()
----> 1 assert False

AssertionError:
  • blocca l'esecuzione del programma
  • lancia un'eccezione di tipo AssertionError
  • nota come "dopo" non venga stampato

Esempio assert¶

Se a un certo punto del programma:

  • voglio usare della farina
  • mi aspetto di avere almeno 1 chilo
  • in caso contrario voglio interrompere l'esecuzione

posso scrivere così:

assert farina > 1.0

Una torta assertiva¶

In [7]:
 
Out[7]:
Python Tutor visualization

assert¶

Pensati principalmente per debuggare, verificare condizioni 'al volo'

bello ma... e la performance?

  • esecuzione assert disabilitabile con -O
python -O mio_file.py

assert vs raise Exception()¶

se volete solo debuggare (e magari disabilitare in seguito):

assert farina >= 1.0

per controlli che volete fare sempre:

if not farina >= 1.0:
    raise Exception("Manca la farina")

Perche testare?¶

dove è il tuo software

Tutto a posto?¶

In [8]:
def somma(x, y):
    """ Prende due numeri e RITORNA la loro somma
    """
    s = x + 3
    return s


print(somma(5,3))
print(somma(3,5)) 
print(somma(1,2)) 
8
6
4

Più espliciti..¶

In [9]:
def somma(x, y):
    """ Prende due numeri e RITORNA la loro somma
    """
    s = x + 3
    return s


print(somma(5,3), 'mi attendo 8')
print(somma(3,5), 'mi attendo 8') 
print(somma(1,2), 'mi attendo 3') 
8 mi attendo 8
6 mi attendo 8
4 mi attendo 3

Automatizzare con assert¶

assert si può usare per testare il codice

Guarda in Python Tutor¶

def somma(x, y):
    """ Prende due numeri e RITORNA la loro somma
    """
    s = x + 3
    return s


assert somma(5,3) == 8
assert somma(3,5) == 8
assert somma(1,2) == 3
print("Congratulazioni! Hai passato tutti i test!")
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
/tmp/ipykernel_6319/3657306284.py in <module>
      7 
      8 assert somma(5,3) == 8
----> 9 assert somma(3,5) == 8
     10 assert somma(1,2) == 3
     11 print("Congratulazioni! Hai passato tutti i test!")

AssertionError:

Ci prendiamo licenze...¶

Nota 1: ci sono soluzioni migliori per testare, esempi:

  • PyTest recente e pulito (a sua volta usa assert)

  • Unittest: la libreria di testing di default di Python, approccio più tradizionale

Nota 2: noi mettiamo i test nello stesso file che stiamo testando, ma di solito vengono messi in un file separato apposito

  • Perchè?
In [10]: