Python Încercați, cu excepția: explicat cu exemple

În limbajul Python, structura `try-except` este un mecanism esențial pentru a gestiona erorile într-un mod elegant, prevenind blocarea bruscă a programului.

O gestionare eficientă a excepțiilor contribuie la crearea unui cod mai robust și mai puțin predispus la eșecuri. Acest articol își propune să ofere o înțelegere aprofundată a gestionării excepțiilor, împreună cu scenarii concrete unde aceasta este deosebit de utilă. Mai mult, vom analiza și modul în care putem genera noi excepții.

Ce înseamnă gestionarea excepțiilor?

Excepțiile reprezintă anomalii sau erori critice ce pot apărea în timpul execuției unui program. Atunci când nu sunt gestionate corespunzător, aceste excepții pot determina oprirea neașteptată a programului. Prin urmare, gestionarea excepțiilor este o modalitate de a preîntâmpina aceste situații și de a asigura funcționarea continuă a programului.

Să vedem un exemplu practic care ilustrează apariția unei excepții:

user_input = input("Introduceți un număr: ")
num = int(user_input)
print("Dublul numărului este:", num * 2)

La prima vedere, codul de mai sus pare perfect funcțional. Acesta solicită utilizatorului să introducă un număr, îl transformă într-un număr întreg și apoi afișează dublul acelui număr.

Dacă rulăm programul și introducem valoarea 5, totul decurge conform așteptărilor. Însă…

Dar, să presupunem că reluăm execuția programului. De data aceasta, în loc de 5, introducem textul „salut”. Programul va genera o eroare. Deoarece șirul „salut” nu poate fi convertit într-un număr întreg, se generează o excepție, iar programul se oprește brusc.

De ce apar excepțiile și de ce este important să le gestionăm?

Excepțiile apar adesea ca urmare a structurării programelor în funcții. Aceste funcții sunt apelate pentru a îndeplini diverse sarcini.

În exemplul anterior, am apelat funcția `input` pentru a prelua datele de la utilizator, apoi am apelat funcția `int` pentru a transforma intrarea într-un număr întreg, iar în final, am folosit funcția `print` pentru a afișa rezultatul.

Pe parcursul desfășurării operațiilor lor, funcțiile pot întâmpina erori pe care nu le pot gestiona direct. În aceste situații, funcțiile trebuie să întrerupă executarea și să semnaleze apariția unei erori. Această semnalizare se realizează prin generarea unei excepții.

Codul care a apelat funcția este responsabil pentru a monitoriza aceste excepții și pentru a reacționa corespunzător. Dacă nu se face acest lucru, programul se va opri în mod neașteptat atunci când întâmpină erori, așa cum am văzut în exemplul anterior.

Așadar, excepțiile constituie un mecanism de comunicare prin care o funcție apelată poate transmite un semnal de avertizare către codul care a inițiat apelul. Iar reacția potrivită la acest semnal este, de fapt, esența gestionării excepțiilor.

Tipuri diferite de excepții

Este important să înțelegem că nu toate excepțiile sunt identice. Există diverse tipuri de excepții generate pentru erori diferite. De exemplu, dacă încercăm să împărțim un număr la zero, apare o eroare de tip `ZeroDivisionError`. Iar o eroare `TypeError` apare atunci când încercăm să efectuăm o operație cu un tip de date necorespunzător. Puteți găsi o listă completă a tipurilor de excepții.

Cum gestionăm excepțiile

După cum am explicat mai sus, excepțiile sunt semnale de avertizare transmise de funcțiile pe care le apelăm. Prin urmare, codul nostru trebuie să fie capabil să recepționeze aceste semnale și să acționeze în mod adecvat. Pentru a gestiona excepțiile în mod eficient, folosim structura `try-except` din Python. Structura de bază a acestei construcții este următoarea:

try:
    # Codul care ar putea genera o excepție
except:
    # Codul care se execută în cazul apariției unei excepții
finally:
    # Codul care se execută indiferent dacă a apărut sau nu o excepție

După cum se observă, această construcție este formată din trei cuvinte cheie, fiecare având rolul său specific:

`try`

Cuvântul cheie `try` marchează începutul unei structuri `try-except`. El indică un bloc de cod care ar putea genera o excepție. Acesta reprezintă o instrucțiune către interpretorul Python de a încerca să execute codul din acel bloc. În cazul apariției unei excepții, execuția programului este imediat oprită în acel punct, iar fluxul este direcționat către blocul `except`.

`except`

Cuvântul cheie `except` marchează blocul de cod care va fi executat în cazul în care o excepție este generată în timpul execuției blocului `try`. Se pot defini mai multe blocuri `except` pentru a gestiona diferite tipuri de excepții. Acest aspect va fi exemplificat în continuare.

`finally`

Cuvântul cheie `finally` este al treilea și ultimul cuvânt cheie utilizat în construcția `try-except` din Python. El indică un bloc de cod care se va executa indiferent dacă o excepție a fost sau nu generată.

Un exemplu practic

Iată un exemplu care ilustrează modul în care cuvintele cheie de mai sus pot gestiona o excepție. Vom modifica exemplul inițial în următorul mod:

try:
    user_input = input("Introduceți un număr: ")
    num = int(user_input)
    print("Dublul numărului este:", num * 2)
except:
    print("A apărut o problemă.")
finally:
    print("Acest cod se va executa oricum.")

Dacă rulăm codul de mai sus cu valoarea validă 5, vom obține următoarea ieșire:

Iar dacă introducem valoarea „salut”, vom obține următoarea ieșire:

Astfel, observăm că în cazul în care nu a fost generată nicio excepție în timpul execuției blocului `try`, programul a trecut la blocul `finally`. În schimb, când a apărut o excepție în blocul `try`, programul a sărit la blocul `except` și apoi la blocul `finally`.

De asemenea, putem gestiona excepții specifice anumitor tipuri de erori. Spre exemplu, dacă dorim să gestionăm diferit excepțiile de tip `ValueError` și `KeyboardInterrupt`, putem modifica codul astfel:

try:
    user_input = input("Introduceți un număr: ")
    num = int(user_input)
    print("Dublul numărului este:", num * 2)
except ValueError:
    print("Valoarea introdusă nu poate fi convertită în număr întreg.")
except KeyboardInterrupt:
    print("A fost primită o întrerupere de la tastatură.")
except:
    print("Bloc general de gestionare a excepțiilor.")
finally:
    print("Acest cod se va executa oricum.")

În codul de mai sus, avem trei blocuri `except`. Primul bloc gestionează doar excepțiile `ValueError`, al doilea gestionează doar excepțiile `KeyboardInterrupt`, iar ultimul bloc `except`, fără un tip de excepție asociat, este un bloc generic care va captura orice altă excepție negestionată de primele două blocuri.

Dacă rulăm acest cod, ar trebui să obținem rezultate similare cu următoarele:

Atunci când o excepție este generată, putem obține informații suplimentare despre aceasta prin intermediul obiectului excepție. Pentru a accesa acest obiect, folosim cuvântul cheie `as`, după cum urmează:

try:
    user_input = input("Introduceți un număr: ")
    num = int(user_input)
    print("Dublul numărului este:", num * 2)
except ValueError as e:
    print("Eroare de valoare:", e)
except KeyboardInterrupt as e:
    print("Întrerupere de la tastatură:", e)
except Exception as e:
    print("Altă excepție:", e)

Cum generăm excepții?

Până acum, am discutat despre gestionarea excepțiilor generate de alte funcții. Cu toate acestea, este posibil să generăm excepții și în codul nostru. Pentru a genera o excepție, folosim cuvântul cheie `raise`. De asemenea, specificăm o clasă care reprezintă tipul de excepție pe care dorim să o generăm, împreună cu un mesaj explicit care poate fi citit de utilizator.

În exemplul următor, folosim clasa `Exception` pentru a genera o excepție generică. Mesajul este transmis constructorului clasei.

raise Exception('A apărut o problemă.')

Dacă rulăm fragmentul de cod de mai sus, vom obține rezultate similare cu următoarele:

Putem specifica și diferite tipuri de excepții. De exemplu, putem genera o excepție `TypeError` atunci când o valoare are un tip de date greșit:

def double(x):
    if isinstance(x, int):
        return x * 2
    else:
        raise TypeError('x ar trebui să fie un număr întreg')

Sau, dacă valoarea specificată depășește limitele acceptabile, putem genera o excepție `ValueError`:

def say_hello(name):
    if name == '':
        raise ValueError('Valoare în afara limitelor.')
    else:
        print('Bună ziua,', name)

De asemenea, putem crea propriile tipuri de excepții prin moștenirea clasei `Exception`. Iată un exemplu:

class InvalidHTTPMethod(Exception):
    pass

În exemplul de mai sus, am creat o clasă `InvalidHTTPMethod` care moștenește de la clasa `Exception`. O putem folosi în mod similar pentru a genera excepții:

raise InvalidHTTPMethod('Trebuie să fie GET sau POST.')

Cazuri de utilizare frecvente pentru gestionarea excepțiilor

Gestionarea excepțiilor este utilă în numeroase scenarii. Exemplul anterior a demonstrat modul în care gestionăm excepțiile cauzate de introducerea datelor de către utilizator. Această secțiune va acoperi alte două situații în care gestionarea excepțiilor este deosebit de utilă: gestionarea excepțiilor apărute ca urmare a solicitărilor de rețea eșuate și gestionarea excepțiilor în timpul citirii fișierelor.

Efectuarea de solicitări de rețea

În exemplul de mai jos, facem o solicitare către Google. Monitorizăm excepțiile pentru a le gestiona. Aceste excepții sunt definite în modulul `requests.exceptions`.

import requests

try:
    response = requests.get("https://google.com")

    # Verificăm dacă codul de stare a răspunsului se află în intervalul 200-299 (răspuns reușit)
    if 200 <= response.status_code < 300:
        print("Solicitarea a avut succes!")
    else:
        print(f"Solicitarea a eșuat cu codul de stare: {response.status_code}")
except requests.exceptions.RequestException as e:
    print(f"A apărut o excepție de tip RequestException: {e}")
except requests.exceptions.ConnectionError as e:
    print(f"A apărut o excepție de tip ConnectionError: {e}")
except requests.exceptions.Timeout as e:
    print(f"A apărut o excepție de tip Timeout: {e}")
except requests.exceptions.TooManyRedirects as e:
    print(f"A apărut o excepție de tip TooManyRedirects: {e}")
except requests.exceptions.HTTPError as e:
    print(f"A apărut o excepție de tip HTTPError: {e}")
except Exception as e:
    print(f"A apărut o eroare neașteptată: {e}")

Citirea datelor dintr-un fișier

În acest ultim exemplu, vom citi date dintr-un fișier numit `hello.txt`. Gestionăm, de asemenea, excepțiile comune care pot fi generate, cum ar fi `FileNotFoundError` și `IOError`.

try:
    with open(file_path, 'r') as file:
        data = file.read()
        print("Conținutul fișierului:")
        print(data)
except FileNotFoundError as e:
    print(f"A apărut o excepție de tip FileNotFoundError: {e}")
except IOError as e:
    print(f"A apărut o excepție de tip IOError: {e}")
except Exception as e:
    print(f"A apărut o eroare neașteptată: {e}")

Concluzie

Acest articol a explorat conceptul de excepții, motivele pentru care acestea apar și importanța gestionării lor pentru a asigura fiabilitatea codului și pentru a preveni blocările. Am analizat modul de gestionare a excepțiilor și, de asemenea, cum să generăm excepții.

Pentru a aprofunda cunoștințele, aruncați o privire și asupra tipurilor comune de erori în Python și a modalităților de remediere a acestora.