015 Python 3 – Errori di sintassi ed eccezioni

21 Gennaio, 2023 (23:16) | Linux | By: sargonsei

015 Python 3 – Errori di sintassi ed eccezioni

Questa sezione degli appunti di Python 3 tratta 2 tipi di errori: gli errori di sintassi, e le eccezioni.

1) Errori di sintassi
Gli errori di sintassi si verificano quando, in fase di scrittura dello script, non viene rispettata la sintassi propria del linguaggio di programmazione; ciò comporta che per “errore di sintassi” si intenda non solo l’errata digitazione di lettere e numeri, ma anche la digitazione di spazi e punteggiatura non conforme a quanto richiesto dal linguaggio di programmazione.
1A) Lettere MAIUSCOLE e lettere minuscole
Python è “case sensitive”, quindi percepisce differenze fra comandi e stringhe scritti in lettere maiuscole o minuscole. Di conseguenza: le variabili ‘ALFA’ ‘Alfa’ ‘aLfa’ e ‘alfa’ sono 4 variabili diverse; le funzioni ‘nomeFunzione’ e ‘nomeFunzione’ sono nomi di funzioni diverse; print() è un comando, mentre Print() è un errore di sintassi.
Seguono alcuni esempi.

>>> alfa = 2; alfa = 3; ALFA = 4; print(alfa, ALFA) # alfa ≠ ALFA
3 4
>>> ALFA = 3; Print(ALFA) # Il comando Print() non esiste
SyntaxError: invalid syntax
>>> 

1B) Rispettare la punteggiatura.

>>> ALFA = "3" print(ALFA) # La sintassi corretta e’ ALFA = "3"; print(ALFA)
SyntaxError: invalid syntax
>>> ALFA = 3, print(ALFA) # La sintassi corretta e’ ALFA = 3; print(ALFA)
Traceback (most recent call last):
  File "", line 1, in 
    ALFA = 3, print(ALFA) # La sintassi corretta e’ ALFA = 3; print(ALFA)
NameError: name 'ALFA' is not defined
>>> ALFA = "3". print(ALFA) # La sintassi corretta e’ ALFA = "3"; print(ALFA)
Traceback (most recent call last):
  File "", line 1, in 
    ALFA = "3". print(ALFA) # La sintassi corretta e’ ALFA = "3"; print(ALFA)
AttributeError: 'str' object has no attribute 'print'
>>> 

1C) Rispettare l’ortografia.

>>> ALFA = 3; pr!nt(ALFA) # pr!nt non è un comando
SyntaxError: invalid syntax
>>> 

1D) Definire le variabili prima di utilizzarle
Contrariamente a quanto si potrebbe pensare, in Python, se vogliamo che una variabile contenga una stringa vuota oppure il numero 0, dobbiamo prima definirla mediante l’esecuzione dell’istruzione VARIABILE = “” oppure VARIABILE = 0; una variabile a cui non è stato dato alcun contenuto non è vuota ma è inesistente, e l’utilizzo di una variabile inesistente produce un messaggio di errore seguito dal blocco dell’esecuzione di Python.

>>> print(A + "uno") # La sintassi corretta e’ A = ""; print(A + "uno")
Traceback (most recent call last):
  File "", line 1, in 
    print(A + "uno") # La sintassi corretta e’ A = ""; print(A + "uno")
NameError: name 'A' is not defined
>>> print(A + 1) # La sintassi corretta e’ A = 0; print(A + 1)
Traceback (most recent call last):
  File "", line 1, in 
    print(A + 1) # La sintassi corretta e’ A = 0; print(A + 1)
NameError: name 'A' is not defined
>>>

Quando uno script in Python si blocca a causa di un errore di sintassi, il programmatore non può fare altro che visualizzare il programma, cercare gli errori, e correggerli.

2) Eccezioni
Talvolta può accadere che una riga di comandi formalmente corretta dia luogo ad un errore; quando ciò avviene, l’errore che si verifica è detto ‘eccezione’. Si veda ad esempio lo script CALCOLATRICE da me creato usando il linguaggio Python 3 e salvato sia alla pagina web
http://sargonsei.altervista.org/012-Python3/
sia all’interno del file di testo che io ho salvato col nome ‘012-Calcolatrice.py’ (ma avrei potuto usare qualsiasi altro nome) salvato nella cartella di lavoro (ma avrei potuto salvarlo in qualsiasi altra cartella), quindi, per lanciarlo, io non devo fare altro che aprire un terminale nella cartella di lavoro ed impartire il comando
[python3 012-Calcolatrice.py] # (senza parentesi quadre).
Una volta avviato lo script, vediamo che funziona senza intoppi, con 2 eccezioni.

2A) ‘ValueError’: Il programma aspettava l’inserimento di un valore numerico.
Quando lo script ci chiede uno qualsiasi dei numeri da sommare, o sottrarre, o moltiplicare, o dividere, nel caso in cui l’utente inserisca un dato non numerico, compare un messaggio di errore ‘ValueError’, e lo script si ferma.
Nel caso specifico in cui si voglia eseguire una sottrazione e, quando viene richiesto di inserire il sottraendo, si digita [UNO] (senza parentesi quadre), il messaggio che compare prima che lo script si fermi è il seguente:

Traceback (most recent call last):
  File "/mnt/Dati1/Guide/Guida-Python3/012-Calcolatrice.py", line 70, in 
    X2, Y2 = DATI(X1, Y1)     # Chiama la funzione DATI() previo trasferimento del contenuto delle variabili X1 e Y1.
  File "/mnt/Dati1/Guide/Guida-Python3/012-Calcolatrice.py", line 17, in DATI
    YY = float(input(Y))  # Acquisisce il secondo numero e lo mette in una variabile a virgola mobile
ValueError: could not convert string to float: 'UNO'

Il contenuto esatto del messaggio può variare a seconda della versione utilizzata. Nel mio caso, si tratta di un messaggio di 6 righe:
la prima riga avvisa che Python 3 ha sospeso l’esecuzione dello script in Python per rintracciare l’origine dell’errore;
la seconda riga contiene il nome del file (completo del percorso) nel quale l’errore è stato riscontrato, il numero della prima riga coinvolta nell’errore, e la sezione a cui la riga appartiene (in questo caso, il modulo principale);
la terza riga contiene il contenuto della riga suddetta (in questo caso, il contenuto della riga 70);
la quarta riga contiene il nome del file (completo del percorso) nel quale l’errore è stato riscontrato, il numero della seconda riga coinvolta nell’errore, e la sezione a cui la riga appartiene (in questo caso, la funzione DATI);
la quinta riga contiene il contenuto della riga suddetta (in questo caso, il contenuto della riga 17);
la sesta riga contiene il nome specifico dell’errore ed il motivo dell’errore (in questo caso: “non è possibile convertire la stringa UNO’ in un numero a virgola mobile”).
L’interpretazione corretta di queste 6 righe è qualcosa tipo: «L’istruzione presente alla riga 70 ‘chiama’ la funzione DATI(), la quale contiene la riga 17 che prevede che l’utente inserisca un valore numerico, ma l’utente ha inserito un valore non numerico, generando così un ‘ValueError’ ed il programma si è bloccato.»

2B) Gestire l’errore/eccezione ‘ValueError’ usando le istruzioni ‘try’ ed ‘except’.
Esaminando la funzione DATI() del programma ‘012-Calcolatrice.py’, vediamo che è composta dalle 4 righe seguenti:

def DATI(X, Y):          # Inizio definizione funzione DATI()
    XX = float(input(X)) # Acquisisce il primo numero e lo mette in una variabile a virgola mobile
    YY = float(input(Y)) # Acquisisce il secondo numero e lo mette in una variabile a virgola mobile
    return(XX, YY)       # Restituisce i valori contenuti nelle due variabili a virgola mobile

La seconda e la terza riga della funzione DATI() chiedono all’utente di inserire valori numerici e, se l’utente inserisce valori non numerici, si genera l’errore ‘ValueError’ che blocca l’esecuzione del programma.
In questo caso specifico, il blocco del programma può essere evitato modificando il testo della funzione DATI() come segue:

def DATI(X, Y):    # Inizio definizione funzione DATI()
    try:           # Inizio istruzioni soggette a gestione errore
        XX = float(input(X))  # Acquisisce il primo numero e lo mette in una variabile a virgola mobile
        YY = float(input(Y))  # Acquisisce il secondo numero e lo mette in una variabile a virgola mobile
    except ValueError:        # Inizio istruzioni da eseguire in caso di errore 'ValueError'
        print("ERRORE!!! Occorre inserire due numeri.")
        XX, YY = DATI(X, Y)   # Chiama la funzione DATI() previo trasferimento del contenuto delle variabili X e Y.
    return(XX, YY) # Restituisce i valori contenuti nelle due variabili a virgola mobile

Il motivo per cui sono state aggiunte 5 righe, è il seguente:
la riga contenente il comando ‘try:’ indica l’inizio delle istruzioni oggetto della gestione dell’errore;
fra le righe seguenti, l’indentazione identifica le 2 righe soggette alla gestione dell’errore;
la riga contenente il comando ‘except ValueError:’ indica l’inizio delle istruzioni che verranno eseguite esclusivamente nel caso si verifichi l’errore ‘ValueError’
fra le righe seguenti, l’indentazione identifica le 2 righe che verranno eseguite solo ed esclusivamente nel caso in cui durante l’esecuzione delle righe soggette alla gestione dell’errore si verifichi l’errore ‘ValueError’;
quando ciò avviene, la gestione dell’errore/eccezione predisposta dall’utente si sostituisce al comportamento che il programma avrebbe in assenza della gestione dell’errore, quindi, in questo caso, fa apparire il messaggio “ERRORE!!! Occorre inserire due numeri.” e ‘chiama’ nuovamente l’esecuzione della funzione DATI().

2C) ‘ZeroDivisionError’: Un divisore non può valere zero.
Quando lo script ci chiede di inserire il divisore, nel caso in cui l’utente inserisca il numero 0, compare un messaggio di errore ‘ZeroDivisionError’, e lo script si ferma.
Il messaggio che compare prima che lo script si fermi è il seguente:

Traceback (most recent call last):
  File "/mnt/Dati1/Guide/Guida-Python3/012-Calcolatrice.py", line 81, in 
    RISULTATO = DIVISIONE(X2, Y2) # Chiama la funzione DIVISIONE() previo trasferimento del contenuto delle variabili X2 e Y2.
  File "/mnt/Dati1/Guide/Guida-Python3/012-Calcolatrice.py", line 35, in DIVISIONE
    QUOTO = DIVIDENDO / DIVISORE    # Calcola il QUOTO
ZeroDivisionError: float division by zero

Il contenuto esatto del messaggio può variare a seconda della versione utilizzata. Nel mio caso, si tratta di un messaggio di 6 righe:
la prima riga avvisa che Python 3 ha sospeso l’esecuzione dello script in Python per rintracciare l’origine dell’errore;
la seconda riga contiene il nome del file (completo del percorso) nel quale l’errore è stato riscontrato, il numero della prima riga coinvolta nell’errore, e la sezione a cui la riga appartiene (in questo caso, il modulo principale);
la terza riga contiene il contenuto della riga suddetta (in questo caso, il contenuto della riga 81);
la quarta riga contiene il nome del file (completo del percorso) nel quale l’errore è stato riscontrato, il numero della seconda riga coinvolta nell’errore, e la sezione a cui la riga appartiene (in questo caso, la funzione DIVISIONE);
la quinta riga contiene il contenuto della riga suddetta (in questo caso, il contenuto della riga 35);
la sesta riga contiene il nome specifico dell’errore ed il motivo dell’errore (in questo caso: “non è possibile dividere un numero per zero”).
L’interpretazione corretta di queste 6 righe è qualcosa tipo: «L’istruzione presente alla riga 81 ‘chiama’ la funzione DIVISIONE(), la quale contiene la riga 35 che prevede l’esecuzione di una divisione, ma l’utente ha fornito un DIVISORE con valore 0, generando così un ‘ZeroDivisionError’ ed il programma si è bloccato.»

2D) Gestire l’errore/eccezione ‘ZeroDivisionError’ usando le istruzioni ‘try’ ed ‘except’.
Esaminando la funzione DIVISORE() del programma ‘012-Calcolatrice.py’, vediamo che è composta dalle 7 righe seguenti:

def DIVISIONE(DIVIDENDO, DIVISORE): # Inizio definizione funzione DIVISIONE()
    QUOTO = DIVIDENDO / DIVISORE    # Calcola il QUOTO
    QUOZ = DIVIDENDO // DIVISORE    # Calcola il QUOZIENTE
    RESTO = DIVIDENDO % DIVISORE    # Calcola il RESTO
    DIV1 = "\n(Dividendo : Divisore) = Quoto = (Quoziente e Resto)"
    DIV1 = "(" + str(DIVIDENDO) + " : " + str(DIVISORE) + ")= " + str(QUOTO)  + " =(" + str(QUOZ) + " e resto " + str(RESTO) +")" + DIV1
    return(DIV1)

La seconda, la terza, e la quarta riga della funzione DIVISIONE() calcolano il risultato di una divisione e, se l’utente ha inserito un DIVISORE = 0, si genera l’errore ‘ZeroDivisionError’ che blocca l’esecuzione del programma.
In questo caso specifico, il blocco del programma può essere evitato modificando il testo della funzione DATI() come segue:

def DIVISIONE(DIVIDENDO, DIVISORE): # Inizio definizione funzione DIVISIONE()
    try:           # Inizio istruzioni soggette a gestione errore
        QUOTO = DIVIDENDO / DIVISORE    # Calcola il QUOTO
        QUOZ = DIVIDENDO // DIVISORE    # Calcola il QUOZIENTE
        RESTO = DIVIDENDO % DIVISORE    # Calcola il RESTO
        DIV1 = "\n(Dividendo : Divisore) = Quoto = (Quoziente e Resto)"
        DIV1 = "(" + str(DIVIDENDO) + " : " + str(DIVISORE) + ")= " + str(QUOTO)  + " =(" + str(QUOZ) + " e resto " + str(RESTO) +")" + DIV1
    except ZeroDivisionError:        # Inizio istruzioni da eseguire in caso di errore 'ZeroDivisionError'
        DIV1 = "Non è possibile dividere per zero."
    return(DIV1)

Il motivo per cui sono state aggiunte 4 righe, è il seguente:
la riga contenente il comando ‘try:’ indica l’inizio delle istruzioni oggetto della gestione dell’errore;
fra le righe seguenti, l’indentazione identifica le 5 righe soggette alla gestione dell’errore;
la riga contenente il comando ‘except ZeroDivisionError:’ indica l’inizio delle istruzioni che verranno eseguite esclusivamente nel caso si verifichi l’errore ‘except ZeroDivisionError’
fra le righe seguenti, l’indentazione identifica l’unica riga che verrà eseguita solo ed esclusivamente nel caso in cui durante l’esecuzione delle righe soggette alla gestione dell’errore si verifichi l’errore ‘ZeroDivisionError’;
quando ciò avviene, la gestione dell’errore/eccezione predisposta dall’utente si sostituisce al comportamento che il programma avrebbe in assenza della gestione dell’errore, quindi, in questo caso, fa apparire il messaggio “Non è possibile dividere per zero.” ed il programma procede.

2E) Gestire l’errore/eccezione generica usando le istruzioni ‘try’ ed ‘except’.
Quando viene specificato il tipo di errore, il blocco di istruzioni corrispondente all’eccezione viene eseguito solo ed esclusivamente nel caso in cui si verifichi il tipo di errore specificato, e non altri tipi di errore; in altre parole: il blocco di istruzioni relativo a ‘except ValueError:’ viene eseguito solo se si verifica un errore di tipo ‘ValueError’ ed il blocco di istruzioni relativo a ‘except ZeroDivisionError:’ viene eseguito solo se si verifica un errore di tipo ‘ZeroDivisionError’.
Omettendo di specificare il tipo di errore, il blocco di istruzioni corrispondente all’eccezione viene eseguito ogni qual volta si produce un errore, a prescindere dalla tipologia dell’errore stesso; di conseguenza, l’errore eventualmente verificatosi viene rilevato, ma non è possibile produrre una risposta diversa a seconda della tipologia di errore. Qualora, per il medesimo errore, si voglia prevedere sia una risposta per un errore generico, sia una risposta per un errore specifico, la risposta generica deve essere messa alla fine, e verrà eseguita solo nel caso in cui l’errore rilevato non corrisponde all’errore specifico; per esempio:

print("Questo script in Python 3 esegue la divisione fra 2 numeri.")
DIVIDENDO = 0; DIVISORE = 0
try:           # Inizio istruzioni soggette a gestione errore
    DIVIDENDO = float(input("Dividendo? "))  # Acquisisce il Dividendo e lo mette in una variabile a virgola mobile
    DIVISORE = float(input("Divisore? "))  # Acquisisce il Divisore e lo mette in una variabile a virgola mobile
except:        # Inizio istruzioni da eseguire in presenza di errore generico
    print("Errore generico in fase di inserimento dei numeri.")
else:          # Inizio istruzioni da eseguire in assenza di errore generico
    print("Dividendo: ", DIVIDENDO, "\nDivisore: ", DIVISORE)
try:           # Inizio istruzioni soggette a gestione errore
    QUOTO = DIVIDENDO / DIVISORE    # Calcola il QUOTO
    RISULTATO = "(" + str(DIVIDENDO) + " : " + str(DIVISORE) + ")= " + str(QUOTO)
except ZeroDivisionError:        # Inizio istruzioni da eseguire in caso di errore 'ZeroDivisionError'
    RISULTATO = "Non è possibile dividere per zero." # Risposta nel caso di errore specifico
except:        # Inizio istruzioni da eseguire in caso di errore generico
    RISULTATO = "Errore generico in fase di calcolo del risultato della divisione." # Risposta nel caso di errore generico
print(RISULTATO)

.
016 Python3 – Script Calcolatrice con Gestione Errori/Eccezioni
http://sargonsei.altervista.org/016-python3/

Pagina ufficiale di Python 3 dedicata alla lista delle eccezioni
https://docs.python.org/3/library/exceptions.html