4.1 Chiusura del file e gestione degli errori
A volte, quando si legge un file, si verificano errori o eccezioni e il file rimane non chiuso. Questo può potenzialmente portare a una perdita di memoria o perdita di file handler
. Pertanto, il lavoro con il file deve essere racchiuso in un blocco try-except
.
Supponiamo che tu abbia il codice:
file = open('example.txt', 'a') # Apriamo il file
file.write("This is a new line added to the file.")
file.close() # Chiudiamo il file
Deve essere incapsulato in un blocco try-except
:
try:
file = open('example.txt', 'a') # Apriamo il file
file.write("This is a new line added to the file.")
file.close() # Chiudiamo il file
except FileNotFoundError:
print("File not found")
file.close() # Chiudiamo il file
Per evitare di scrivere due volte il metodo close()
, puoi metterlo in un blocco finally
:
try:
file = open('example.txt', 'a') # Apriamo il file
file.write("This is a new line added to the file.")
except FileNotFoundError:
print("File not found")
finally:
file.close() # Chiudiamo il file
Questo codice sembra bello, ma... non funziona, perché la variabile file
è definita solo nel blocco try
e non è accessibile dai blocchi except
e finally
.
Quindi dobbiamo definire la variabile a un livello superiore:
file = None
try:
file = open('example.txt', 'a') # Apriamo il file
file.write("This is a new line added to the file.")
except FileNotFoundError:
print("File not found")
finally:
file.close() # Chiudiamo il file
Questa soluzione è migliore, ma ha anche dei difetti. Ad esempio, se il file non viene mai aperto, la variabile file
rimarrà None
. E allora tentare di chiudere il file genererà un errore — ci sarà un tentativo di chiamare il metodo close()
su un oggetto inesistente.
Quindi dobbiamo aggiungere un controllo prima di chiamare il metodo close()
:
file = None
try:
file = open('example.txt', 'a') # Apriamo il file
file.write("This is a new line added to the file.")
except FileNotFoundError:
print("File not found")
finally:
if file:
file.close() # Chiudiamo il file
Se sei sorpreso che 3 righe di codice si siano trasformate in 9, non sei solo. Fortunatamente, c'è già una soluzione pronta a questo problema, di cui parleremo di seguito.
4.2 Operatore with
L'operatore with
in Python fornisce un modo conveniente di gestire le risorse, come i file, assicurando che vengano chiuse automaticamente dopo il completamento del blocco with
. Questo semplifica il codice e previene le perdite di risorse, come i file non chiusi.
Sintassi generale dell'operatore with
:
with espressione as variabile:
lavoro con la variabile
L'operatore with
viene utilizzato per incapsulare l'esecuzione di un blocco di codice con un gestore di contesto. Alla utilizzo dell'operatore with
, Python chiama automaticamente i metodi __enter__()
e __exit__()
dell'oggetto gestore di contesto, il che semplifica la gestione delle risorse.
Esempio di utilizzo di with
per lavorare con i file:
with open('example.txt', 'w') as file:
file.write("Hello, World!\n")
file.write("This is a test file.\n")
In questo esempio, il file example.txt
viene aperto in modalità scrittura, e il nome del file viene associato alla variabile file
. Il blocco di codice all'interno del with
chiude automaticamente il file dopo aver completato tutte le operazioni di scrittura.
4.3 Chiusura automatica del file
Uno dei principali vantaggi dell'utilizzo dell'operatore with
è la chiusura automatica del file dopo il completamento del blocco di codice. Ciò avviene anche in caso di eccezione, rendendo il codice più sicuro e affidabile.
Esempio di chiusura automatica del file:
try:
with open('example.txt', 'w') as file:
file.write("Hello, World!\n")
file.write("This is a test file.\n")
# Eccezione per verificare che il file si chiuda comunque
raise Exception("Something went wrong")
except Exception as e:
print(f"Caught an exception: {e}")
# Qui il file è già chiuso
Usa sempre l'operatore with
quando lavori con i file. È semplice e ti aiuta a evitare una miriade di errori.
4.4 Come funziona l'operatore with
Alla base del funzionamento dell'operatore with
vi sono i metodi __enter__()
e __exit__()
, che devono essere implementati nella classe utilizzata come gestore di contesto.
Affinché un oggetto possa essere utilizzato con l'operatore with
, deve implementare i metodi __enter__()
e __exit__()
. Questi metodi definiscono il comportamento all'ingresso e all'uscita del contesto.
Metodo __enter__()
Il metodo __enter__()
è chiamato all'entrata nel blocco with
. Esegue l'inizializzazione della risorsa e restituisce un oggetto che sarà assegnato alla variabile specificata dopo as
.
Metodo __exit__()
Il metodo __exit__()
è chiamato alla fine del blocco with
. Esegue operazioni di pulizia come il rilascio delle risorse o la chiusura dei file. Il metodo accetta tre argomenti: exc_type
, exc_val
e exc_tb
, che contengono informazioni sull'eccezione, se si verifica.
-
exc_type
: il tipo di eccezione (ad esempio,ZeroDivisionError
). -
exc_val
: il valore dell'eccezione (ad esempio, il messaggio di errore). exc_tb
: la traccia dello stack dell'eccezione.
Se il metodo __exit__()
restituisce True
, l'eccezione sarà soppressa. Se restituisce False
, l'eccezione sarà nuovamente sollevata.
Esempio:
class MyContextManager:
def __enter__(self):
print("Entering the context")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type:
print(f"An exception occurred: {exc_type}, {exc_val}")
return False # L'eccezione non viene soppressa
with MyContextManager() as manager:
print("Inside the with block")