Python Python

Excepțiile în Python: Ce sunt și cum le folosim

grivel Jul 01, 2026 10.0

1. Ce sunt Excepțiile?

În Python, o excepție este un obiect care reprezintă un eveniment anormal care apare în timpul execuției programului. Aceste obiecte sunt instanțe ale claselor care derivă din clasa bază BaseException.

De ce este util să le folosim?
1. Separarea responsabilităților: Codul "normal" (logică de business) nu trebuie să fie amestecat cu codul de "gestionare a erorilor".
2. Propagarea informației: Excepțiile transportă date (mesaje, tipul erorii, contextul) de la punctul în care a apărut problema până la punctul în care o gestionăm.
3. Controlul fluxului: Permite programului să continue execuția după ce o eroare a fost gestionată, în loc să se termine abrupt.

2. Structura de bază: try, except, else, finally

Sintaxa standard pentru gestionarea excepțiilor este un bloc try. Dar nu doar try și except. Python oferă două blocuri suplimentare foarte utile: else și finally.

try:
    # Blocul 1: Codul care poate genera excepție
    f = open("fisier_test.txt", "r")
    content = f.read()
except FileNotFoundError:
    # Blocul 2: Codul care se execută DOAR dacă apare FileNotFoundError
    print("Fișierul nu a fost găsit!")
else:
    # Blocul 3: Codul care se execută DOAR dacă NU a apărut nicio excepție
    print("Fișierul a fost citit cu succes. Conținut:", content)
finally:
    # Blocul 4: Codul care se execută ÎNTOTDEAUNA, indiferent dacă a fost excepție sau nu
    f.close()
  • try::
    • Aici introducem codul "riscant". Python va monitoriza executarea liniilor de mai jos.
  • f = open("fisier_test.txt", "r"):
    • Încercăm să deschidem un fișier. Dacă fișierul nu există, Python va genera (arunca) o excepție FileNotFoundError.
  • content = f.read():
    • Dacă fișierul există, citim conținutul.
  • except FileNotFoundError::
    • Specificăm tipul exact de excepție pe care vrem să îl prindem. Python verifică: "A apărut FileNotFoundError? Dacă da, execută acest bloc."
    • Notă importantă: Dacă ar fi apărut o altă excepție (ex: PermissionError), acest bloc nu s-ar activa.
  • print("Fișierul nu a fost găsit!"):
    • Gestionăm eroarea specifică.
  • else::
    • Acest bloc este opțional, dar foarte util. Se execută doar dacă blocul try s-a finalizat fără excepții.
    • De ce? Pentru a separa logica de succes de logica de eroare. Dacă punem codul de procesare a datelor în else, știm sigur că datele sunt valide.
  • print("Fișierul a fost citit..."):
    • Afișăm rezultatul, deoarece funcția open a reușit.
  • finally::
    • Acest bloc este recomandat de folosit pentru resurse (fișiere, conexiuni DB). Se execută ÎNTOTDEAUNA.
    • De ce? Chiar dacă a apărut o excepție în try, sau dacă am sărit în except, finally se va executa. Este ideal pentru a curăți resursele alocate (close(), disconnect()).
  • f.close():
    • Închidem fișierul. Dacă fișierul nu a fost deschis (pentru că a fost FileNotFoundError), această linie ar genera o eroare AttributeError. Aici trebuie să fim atenți! (Vom vedea cum să evităm asta mai jos).

3. Ierarhia Excepțiilor

Toate excepțiile în Python formează un arbore ierarhic. Clasa de bază este BaseException. Sub ea se află Exception (majoritatea erorilor pe care le gestionăm noi). Sub Exception se află clase specifice.

# Arbore simplificat:
# BaseException
#   |-- SystemExit
#   |-- KeyboardInterrupt
#   |-- GeneratorExit
#   |-- Exception  <-- Noi lucrăm aici
#       |-- ValueError
#       |-- TypeError
#       |-- FileNotFoundError
#       |-- ...

De ce este important să înțelegem ierarhia? Poți prinde o excepție generică și vei prinde toate subtipurile ei.

try:
    int("abc")
except Exception as e:
    print(f"Am prins o eroare generică: {type(e).__name__}")
  • int("abc"): Încercăm să convertim string-ul "abc" în int. Aceasta va genera ValueError.
  • except Exception as e:: Prindem Exception. Deoarece ValueError este o subclasă a Exception, eroarea va fi prinsă.
  • print(f"Am prins... {type(e).__name__}"):
    • type(e): Returnează clasa obiectului excepție (ex: <class 'ValueError'>).
    • .__name__: Returnează numele clasei ca string ("ValueError").
    • Astfel, vedem exact ce tip de eroare a fost, chiar dacă am prins-o generic.

4. Crearea unei Excepții Noi

Când erorile standard nu sunt suficient de descriptive, creăm clase proprii. O excepție custom trebuie să moștenească din Exception (sau BaseException).

class EroareLogin(Exception):
    """
    Excepție personalizată pentru erori de autentificare.
    """
    def __init__(self, username, mesaj="Autentificare eșuată"):
        self.username = username
        self.mesaj = mesaj
        # Apelăm constructorul clasei părinte (Exception)
        super().__init__(self.mesaj)
  • class EroareLogin(Exception)::
    • Definim o nouă clasă. Moștenirea din Exception este crucială. Dacă nu o facem, Python nu o va recunoaște ca excepție și nu o poți prinde în except.
  • def __init__(self, username, mesaj="Autentificare eșuată")::
    • Constructorul. Aici definim parametrii excepției.
    • username: Parametru specific pentru contextul login.
    • mesaj="Autentificare eșuată": Parametru default. Dacă utilizatorul nu specifică un mesaj, se folosește acesta.
  • self.username = username:
    • Salvăm username-ul în instanța excepției. Astfel, când prindem excepția, putem accesa e.username.
  • self.mesaj = mesaj:
    • Salvăm mesajul.
  • super().__init__(self.mesaj):
    • Cel mai important pas! super() se referă la clasa părinte (Exception).
    • Excepțiile standard afișează un mesaj când sunt printate. Noi trebuie să transmitem acest mesaj către constructorul părinte. Fără aceasta, printarea excepției ar fi goală sau ar arăta doar obiectul.

5. Cum Aruncăm (Raise) Excepțiile

Acum să simulăm un sistem de login care folosește această excepție.

def autentificare(user, parola):
    if parola != "secret":
        # Aruncăm excepția custom
        raise EroareLogin(user, f"Parola incorectă pentru {user}")

    return "Login reușit"

# Testul
try:
    rezultat = autentificare("alice", "1234")
except EroareLogin as e:
    print(f"Eroare pentru user: {e.username}")
    print(f"Detalii: {e.mesaj}")
  • if parola != "secret":: Verificăm condiția de eroare.
  • raise EroareLogin(user, f"..."):
    • raise: Cuvântul cheie care "aruncă" excepția. Întrerupe execuția curentă și propagă excepția.
    • EroareLogin(user, f"..."): Creăm instanța. Trecem user și un mesaj dinamic.
  • except EroareLogin as e::
    • Prindem exact tipul nostru custom.
  • print(f"Eroare pentru user: {e.username}"):
    • Accesăm atributul username pe care l-am salvat în __init__.
  • print(f"Detalii: {e.mesaj}"):
    • Accesăm atributul mesaj.

6. Gestionarea Parametrilor Default și Flexibilitate

Ce se întâmplă dacă aruncăm excepția fără a specificat mesajul? Python folosește default-ul.

def autentificare_rapida(user):
    # Simulăm o eroare fără detalii extra
    raise EroareLogin(user)

try:
    autentificare_rapida("bob")
except EroareLogin as e:
    print(f"User: {e.username}")
    print(f"Mesaj Default: {e.mesaj}")
  • raise EroareLogin(user): Trecem doar user. mesaj rămâne valoarea default "Autentificare eșuată".
  • print(f"Mesaj Default: {e.mesaj}"): Afișăm mesajul default, demonstrând că parametrul default funcționează.

7. Prinderea Multiplelor Excepții și Excepția Generică

Uneori, nu știi exact ce eroare poate apărea, sau vrei să gestionezi mai multe tipuri în același bloc.

try:
    # Cod care poate genera ValueError sau TypeError
    valoare = int("abc") + "string"
except (ValueError, TypeError) as e:
    print(f"Am prins o eroare de tip {type(e).__name__}: {e}")
except Exception as e:
    print(f"Am prins o altă eroare generică: {e}")
  • except (ValueError, TypeError) as e::
    • Putem specifica o tuplă de excepții. Dacă apare orice dintre ele, acest bloc se activează.
  • except Exception as e::
    • Acest bloc este un "catch-all" (prinde tot). Se execută dacă nu a fost prindă de blocurile anterioare.
    • Ordinea este crucială! Blocurile specifice (ValueError) trebuie să vină înainte de blocul generic (Exception). Python verifică în ordine. Dacă pui Exception primul, vei prinde toate erorile, iar blocurile specifice de mai jos nu se vor activa niciodată.

8. Propagarea Excepțiilor (Reraise)

Uneori, vrei să prindeți eroarea, să logați ceva, dar să o propagi mai departe, ca să fie gestionată la un nivel superior.

def nivel_superior():
    try:
        nivel_inferior()
    except EroareLogin as e:
        print(f"Logare: Eroare detectată la nivel inferior pentru {e.username}")
        raise e  # Re-aruncăm excepția, păstrând contextul
  • raise e:
    • Re-aruncăm excepția prindă. Astfel, gestionarea finală (la un nivel mai sus) va primi aceeași instanță e, cu username și mesaj intacte.
    • Fără raise e, excepția s-ar "consuma" și nu s-ar propagi.

9. Best Practices: Ce NU să faci

  1. Nu prinde Exception generic fără motiv: Poți ascunde bug-uri grave.
  2. Nu ignora excepția (pass):

    try:
        f = open("file.txt")
    except:
        pass  # BAD! Ignori eroarea
    
    • Dacă nu faci pass, fișierul f nu este definit. Orice linie după except care folosește f va genera NameError.
  3. Folosește finally pentru resurse: Nu închide fișierele în except sau else. Folosește finally.

10. Exemplu Complet Executabil: Sistem de Gestionare a Resurselor

Hai să combinăm totul într-un exemplu robust.

class EroareResursa(Exception):
    """Excepție pentru probleme cu resursele."""
    def __init__(self, resursa_id, tip_eroare="Eroare generica"):
        self.resursa_id = resursa_id
        self.tip_eroare = tip_eroare
        super().__init__(f"Resursa {resursa_id}: {tip_eroare}")

def deschide_resursa(resursa_id):
    if resursa_id < 0:
        raise EroareResursa(resursa_id, "ID invalid")
    print(f"Resursa {resursa_id} deschisă.")
    return resursa_id

def procesare_resursa(resursa_id):
    try:
        # 1. Deschidem resursa
        id_valid = deschide_resursa(resursa_id)

        # 2. Procesăm (simulăm o eroare de calcul)
        rezultat = 10 / id_valid

    except ZeroDivisionError:
        # 3. Prindem eroare specifică
        print(f"Eroare matematică pentru resursa {resursa_id}")
        raise EroareResursa(resursa_id, "Diviziune la zero") from ZeroDivisionError

    except EroareResursa as e:
        # 4. Prindem eroarea custom (propagată)
        print(f"Gestionare custom: {e}")

    else:
        # 5. Dacă nu a fost eroare
        print(f"Rezultat succes: {rezultat}")

    finally:
        # 6. Curățenie
        print(f"Resursa {resursa_id} închisă.")

# Test 1: ID valid
print("--- Test 1 ---")
procesare_resursa(5)

# Test 2: ID invalid
print("--- Test 2 ---")
procesare_resursa(-1)

# Test 3: ID 0 (Diviziune la zero)
print("--- Test 3 ---")
procesare_resursa(0)

Explicație detaliată a fluxului:

  1. Test 1 (ID 5):

    • deschide_resursa(5): Reușește.
    • 10 / 5: Reușește.
    • else: Se execută. Afișează rezultatul.
    • finally: Închide resursa.
  2. Test 2 (ID -1):

    • deschide_resursa(-1): Aruncă EroareResursa.
    • except ZeroDivisionError: Nu se activează (nu e diviziune la zero).
    • except EroareResursa as e: Se activează. Afișează mesajul custom.
    • else: Nu se activează (a fost excepție).
    • finally: Închide resursa (chiar și după excepție!).
  3. Test 3 (ID 0):

    • deschide_resursa(0): Reușește (ID >= 0).
    • 10 / 0: Aruncă ZeroDivisionError.
    • except ZeroDivisionError: Se activează.
    • raise EroareResursa(...) from ZeroDivisionError:
      • Aruncăm o excepție custom.
      • from ZeroDivisionError: Chain Exception. Legăm excepția custom de cea originală. Astfel, traceback-ul va arăta că EroareResursa a fost cauzată de ZeroDivisionError. Este foarte util pentru debugging.
    • except EroareResursa as e: Se activează (excepția custom a fost propagată).
    • else: Nu se activează.
    • finally: Închide resursa.

Concluzie

Excepțiile în Python sunt un mecanism puternic pentru:
1. Gestionarea erorilor (try-except).
2. Separarea logicii (else pentru succes, except pentru eroare).
3. Curățenia (finally pentru resurse).
4. Personalizarea (Clase custom cu parametri).
5. Debugging (Propagare și chaining de excepții).

You need to be logged in to access the cloud lab.

Log in