Imparare Python da zero #06: ancora su files ed eccezioni e statement “with”

Nei due post precedenti abbiamo parlato di gestione delle eccezioni e operazioni di lettura e scrittura sui files. In questo post riprendiamo questi due topic e partiamo da li per introdurre lo statement with, il cui obbiettivo è quello di semplificare le operazioni di accesso ai files, risparmiando allo sviluppatore le operazioni di chiusura delle risorse (e quindi qualche riga di codice ripetitiva).
Riprendiamo l’esempio del post precedente:

m = int(input("Inserisci il valore che vuoi moltiplicare: "))
try:
    num = open("numbers.txt")
    for single_line in num:
        print(int(single_line) * m)
    num.close()
except FileNotFoundError:
    print("File NON trovato!")

e lo riscriviamo utilizzando lo statement with:

m = int(input("Inserisci il valore che vuoi moltiplicare: "))
try:
    with open("numbers.txt") as in_file
    for single_line in num:
        print(int(single_line) * m)
except FileNotFoundError:
    print("File NON trovato!")

Python with statement

Come possiamo vedere dal codice risultante lo statement with si occupa del rilascio delle risorse, mentre resta comunque a carico dello sviluppatore la gestione di eventuali eccezioni che si possono verificare nell’interazione con il file.
Nell’esempio precedente lo statement with è utilizzato per la gestione di un’unica risorsa. In realtà è possibile affidare la gestione di più risorse ad un unico “with”, separandole da una virgola. Nell’esempio seguente apriamo due file con un with:

m = int(input("Inserisci il valore che vuoi moltiplicare: "))
try:
    with open("numbers.txt") as inputfile, open("output_numbers.txt", "w") as outputfile:        
    	for single_line in inputfile:
            print(int(single_line) * m, file=outputfile)
except FileNotFoundError:
    print("File NON trovato!")

Python with statement due risorse

In questo caso il suo utilizzo si rivela ancora più prezioso, perchè ci risparmia la scrittura del codice per la chiusura di due risorse.
Vediamo ora invece come utilizzare l’oggetto creato quando si verifica un’eccezione per fornire maggiori informazioni riguardo al tipo di errore che si è verificato.
Nell’esempio precedente ci siamo limitati a catturare l’eccezione tramite il costrutto except e a stampare un messaggio di errore. In realtà, tramite la keyword “as”, è possibile assegnare l’oggetto ed utilizzarlo nel blocco di codice incaricato di gestire l’eccezione. Tale oggetto contiene informazioni aggiuntive e specifiche sulla tipologia di errore che si è verificato.
Supponiamo ad esempio che il file di output, “output_numbers.txt” esista già ma abbia i permessi impostati in modalità read-only e sia quindi protetto da scrittura.
Da shell usiamo il comando chmod per impostare tali permessi nel modo seguente:

chmod 444 output_numbers.txt

In questo modo settiamo i permessi di sola lettura per tutti (proprietario, gruppo ed altri).
Proviamo ad eseguire nuovamente il codice precedente ed otteniamo il seguente risultato:

Traceback (most recent call last):
  File "/Users/Dede/Documents/Python/Python-06/with_test.py", line 3, in 
    with open("numbers.txt") as inputfile, open("output_numbers.txt", "w") as outputfile:
PermissionError: [Errno 13] Permission denied: 'output_numbers.txt'

Python with statement permission denied error

A questo punto abbiamo 2 possibilità:

  • Aggiungere un nuova clausola except per gestire un PermissionError
  • Rendere più generico il tipo catturato nell’unico except (IOError) e specificare l’errore tramite l’oggetto creato

Per implementare il primo caso, come avevamo visto nel post precedente, basta aggiungere la nuova clausola a valle di quella esistente:

m = int(input("Inserisci il valore che vuoi moltiplicare: "))
try:
    with open("numbers.txt") as inputfile, open("output_numbers.txt", "w") as outputfile:
        for single_line in inputfile:
            print(int(single_line) * m, file=outputfile)
except FileNotFoundError:
    print("File NON trovato!")
except PermissionError:
    print("Permessi insufficienti per scrivere sul file!")

Python with multiple except

Per implementare la seconda soluzione invece dobbiamo introdurre l’utilizzo della keyword “as” per assegnare l’oggetto creato nel caso si verifichi un’eccezione. Tale oggetto contiene le informazioni dettagliate dell’errore che possiamo utilizzare dopo aver fatto il casting dell’oggetto a stringa tramite la funzione built-in str.
Modifichiamo il codice come segue:

m = int(input("Inserisci il valore che vuoi moltiplicare: "))
try:
    with open("numbers.txt") as inputfile, open("output_numbers.txt", "w") as outputfile:
        for single_line in inputfile:
            print(int(single_line) * m, file=outputfile)
except IOError as ecc:
    print("Si è verificato un ERRORE: " + str(ecc))

In questo caso, qualunque errore di IO che si verifichi viene gestito da un’unica clausola except e il dettaglio dell’errore specifico viene mostrato dinamicamente, estraendo tale informazione direttamente dall’oggetto “ecc”.

Proviamo ad eseguire il codice:
Python with statement except as

Ripristiniamo ora i permessi di scrittura al file “output_numbers.txt” eseguendo un “chmod 755” e modifichiamo il codice in modo che cerchi di aprire un file di input che non esiste (ad esempio “numb.txt”).

Python with statement except as

Da questi due esempio vediamo come l’oggetto “ecc” ci fornisca sempre le informazioni relative all’esatto errore che si è verificato.

This entry was posted in $1$s. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *